@ -43,6 +43,13 @@
# include "Magnum/Platform/GLContext.h"
# include "Magnum/Platform/GLContext.h"
# endif
# endif
/** @todo drop once we don't support < 1.38.27 anymore */
# ifndef EMSCRIPTEN_EVENT_TARGET_DOCUMENT
# define EMSCRIPTEN_EVENT_TARGET_DOCUMENT reinterpret_cast<const char*>(1)
# define EMSCRIPTEN_EVENT_TARGET_WINDOW reinterpret_cast<const char*>(2)
# define EMSCRIPTEN_EVENT_TARGET_SCREEN reinterpret_cast<const char*>(3)
# endif
namespace Magnum { namespace Platform {
namespace Magnum { namespace Platform {
namespace {
namespace {
@ -174,6 +181,36 @@ namespace {
return Key : : Unknown ;
return Key : : Unknown ;
}
}
std : : string canvasId ( ) {
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
char * id = reinterpret_cast < char * > ( EM_ASM_INT ( {
return allocate ( intArrayFromString ( Module [ ' canvas ' ] . id ) , ' i8 ' , ALLOC_NORMAL ) ;
} ) ) ;
# pragma GCC diagnostic pop
std : : string str = id ;
std : : free ( id ) ;
return str ;
}
bool checkForDeprecatedEmscriptenTargetBehavior ( ) {
/* Emscripten 1.38.27 changed to generic CSS selectors from element IDs
depending on - s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR = 1 being
set .
https : //github.com/emscripten-core/emscripten/pull/7977
There is no simple way to check for compiler options so check
whether the new CSS selectors are being used . If so , it should find
canvas # [ id ] which is any canvas with the ID of Module . canvas .
The old target behavior will look for an element with id = " canvas#[id] "
which could theoretically exist but that ' s highly unlikely . */
bool deprecated = true ;
Vector2d tempSize ;
if ( emscripten_get_element_css_size ( ( " canvas# " + canvasId ( ) ) . data ( ) , & tempSize . x ( ) , & tempSize . y ( ) ) > = 0 ) {
deprecated = false ;
}
return deprecated ;
}
}
}
EmscriptenApplication : : EmscriptenApplication ( const Arguments & arguments ) : EmscriptenApplication { arguments , Configuration { } } { }
EmscriptenApplication : : EmscriptenApplication ( const Arguments & arguments ) : EmscriptenApplication { arguments , Configuration { } } { }
@ -266,10 +303,19 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) {
}
}
# endif
# endif
std : : ostream * verbose = _verboseLog ? Debug : : output ( ) : nullptr ;
_deprecatedTargetBehavior = checkForDeprecatedEmscriptenTargetBehavior ( ) ;
if ( _deprecatedTargetBehavior ) {
Debug { verbose } < < " Platform::EmscriptenApplication::tryCreate(): using old Emscripten target behavior " ;
}
_canvasTarget = ( _deprecatedTargetBehavior ? " " : " # " ) + canvasId ( ) ;
_dpiScaling = dpiScaling ( configuration ) ;
_dpiScaling = dpiScaling ( configuration ) ;
if ( ! configuration . size ( ) . isZero ( ) ) {
if ( ! configuration . size ( ) . isZero ( ) ) {
const Vector2i scaledCanvasSize = configuration . size ( ) * _dpiScaling ;
const Vector2i scaledCanvasSize = configuration . size ( ) * _dpiScaling ;
emscripten_set_canvas_element_size ( " #canvas " , scaledCanvasSize . x ( ) , scaledCanvasSize . y ( ) ) ;
emscripten_set_canvas_element_size ( _canvasTarget . data ( ) , scaledCanvasSize . x ( ) , scaledCanvasSize . y ( ) ) ;
}
}
setupCallbacks ( ! ! ( configuration . windowFlags ( ) & Configuration : : WindowFlag : : Resizable ) ) ;
setupCallbacks ( ! ! ( configuration . windowFlags ( ) & Configuration : : WindowFlag : : Resizable ) ) ;
@ -327,6 +373,17 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
_devicePixelRatio = Vector2 { Float ( emscripten_get_device_pixel_ratio ( ) ) } ;
_devicePixelRatio = Vector2 { Float ( emscripten_get_device_pixel_ratio ( ) ) } ;
Debug { verbose } < < " Platform::EmscriptenApplication: device pixel ratio " < < _devicePixelRatio . x ( ) ;
Debug { verbose } < < " Platform::EmscriptenApplication: device pixel ratio " < < _devicePixelRatio . x ( ) ;
/* Find out which element target strings Emscripten expects. This depends on
the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compiler option . */
_deprecatedTargetBehavior = checkForDeprecatedEmscriptenTargetBehavior ( ) ;
if ( _deprecatedTargetBehavior ) {
Debug { verbose } < < " Platform::EmscriptenApplication::tryCreate(): using old Emscripten target behavior " ;
}
/* Get the canvas ID from Module.canvas, either set by EmscriptenApplication.js
or overridden / manually set by the user . */
_canvasTarget = ( _deprecatedTargetBehavior ? " " : " # " ) + canvasId ( ) ;
/* Get CSS canvas size and cache it. This is used later to detect canvas
/* Get CSS canvas size and cache it. This is used later to detect canvas
resizes in emscripten_set_resize_callback ( ) and fire viewport events ,
resizes in emscripten_set_resize_callback ( ) and fire viewport events ,
because browsers are only required to fire resize events on the window
because browsers are only required to fire resize events on the window
@ -346,10 +403,10 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
}
}
_dpiScaling = dpiScaling ( configuration ) ;
_dpiScaling = dpiScaling ( configuration ) ;
const Vector2i scaledCanvasSize = canvasSize * _dpiScaling * _devicePixelRatio ;
const Vector2i scaledCanvasSize = canvasSize * _dpiScaling * _devicePixelRatio ;
emscripten_set_canvas_element_size ( " #canvas " , scaledCanvasSize . x ( ) , scaledCanvasSize . y ( ) ) ;
emscripten_set_canvas_element_size ( _canvasTarget . data ( ) , scaledCanvasSize . x ( ) , scaledCanvasSize . y ( ) ) ;
/* Create WebGL context */
/* Create WebGL context */
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context ( " #canvas " , & attrs ) ;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context ( _canvasTarget . data ( ) , & attrs ) ;
if ( ! context ) {
if ( ! context ) {
/* When context creation fails, `context` is a negative integer
/* When context creation fails, `context` is a negative integer
matching EMSCRIPTEN_RESULT_ * defines */
matching EMSCRIPTEN_RESULT_ * defines */
@ -371,23 +428,14 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
Vector2i EmscriptenApplication : : windowSize ( ) const {
Vector2i EmscriptenApplication : : windowSize ( ) const {
Vector2d size ;
Vector2d size ;
/* Emscripten 1.38.27 changed to generic CSS selectors from element IDs
emscripten_get_element_css_size ( _canvasTarget . data ( ) , & size . x ( ) , & size . y ( ) ) ;
depending on - s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR = 1 being
set ( which we can ' t detect at compile time ) . Fortunately , using # canvas
works the same way both in the previous versions and the current one .
Unfortunately , this is also the only value that works the same way for
both . Further details at
https : //github.com/emscripten-core/emscripten/pull/7977 */
/** @todo don't hardcode "#canvas" everywhere, make it configurable from outside */
emscripten_get_element_css_size ( " #canvas " , & size . x ( ) , & size . y ( ) ) ;
return Vector2i { Math : : round ( size ) } ;
return Vector2i { Math : : round ( size ) } ;
}
}
# ifdef MAGNUM_TARGET_GL
# ifdef MAGNUM_TARGET_GL
Vector2i EmscriptenApplication : : framebufferSize ( ) const {
Vector2i EmscriptenApplication : : framebufferSize ( ) const {
Vector2i size ;
Vector2i size ;
/* See above why hardcoded */
emscripten_get_canvas_element_size ( _canvasTarget . data ( ) , & size . x ( ) , & size . y ( ) ) ;
emscripten_get_canvas_element_size ( " #canvas " , & size . x ( ) , & size . y ( ) ) ;
return size ;
return size ;
}
}
# endif
# endif
@ -423,12 +471,11 @@ void EmscriptenApplication::swapBuffers() {
/* Called from window resize event but also explicitly from
/* Called from window resize event but also explicitly from
setContainerCssClass ( ) */
setContainerCssClass ( ) */
void EmscriptenApplication : : handleCanvasResize ( const EmscriptenUiEvent * event ) {
void EmscriptenApplication : : handleCanvasResize ( const EmscriptenUiEvent * event ) {
/* See windowSize() for why we hardcode "#canvas" here */
const Vector2i canvasSize { windowSize ( ) } ;
const Vector2i canvasSize { windowSize ( ) } ;
if ( canvasSize ! = _lastKnownCanvasSize ) {
if ( canvasSize ! = _lastKnownCanvasSize ) {
_lastKnownCanvasSize = canvasSize ;
_lastKnownCanvasSize = canvasSize ;
const Vector2i size = canvasSize * _dpiScaling * _devicePixelRatio ;
const Vector2i size = canvasSize * _dpiScaling * _devicePixelRatio ;
emscripten_set_canvas_element_size ( " #canvas " , size . x ( ) , size . y ( ) ) ;
emscripten_set_canvas_element_size ( _canvasTarget . data ( ) , size . x ( ) , size . y ( ) ) ;
ViewportEvent e { event , canvasSize ,
ViewportEvent e { event , canvasSize ,
# ifdef MAGNUM_TARGET_GL
# ifdef MAGNUM_TARGET_GL
framebufferSize ( ) ,
framebufferSize ( ) ,
@ -454,11 +501,7 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
changes . Better than polling for this change in every frame like
changes . Better than polling for this change in every frame like
Sdl2Application does , but still not ideal . */
Sdl2Application does , but still not ideal . */
if ( resizable ) {
if ( resizable ) {
# ifdef EMSCRIPTEN_EVENT_TARGET_WINDOW
const char * target = _deprecatedTargetBehavior ? " #window " : EMSCRIPTEN_EVENT_TARGET_WINDOW ;
const char * target = EMSCRIPTEN_EVENT_TARGET_WINDOW ;
# else
const char * target = " #window " ;
# endif
auto cb = [ ] ( int , const EmscriptenUiEvent * event , void * userData ) - > Int {
auto cb = [ ] ( int , const EmscriptenUiEvent * event , void * userData ) - > Int {
static_cast < EmscriptenApplication * > ( userData ) - > handleCanvasResize ( event ) ;
static_cast < EmscriptenApplication * > ( userData ) - > handleCanvasResize ( event ) ;
return false ; /** @todo what does ignoring a resize event mean? */
return false ; /** @todo what does ignoring a resize event mean? */
@ -466,23 +509,21 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
emscripten_set_resize_callback ( target , this , false , cb ) ;
emscripten_set_resize_callback ( target , this , false , cb ) ;
}
}
/* See windowSize() for why we hardcode "#canvas" here */
emscripten_set_mousedown_callback ( _canvasTarget . data ( ) , this , false ,
emscripten_set_mousedown_callback ( " #canvas " , this , false ,
( [ ] ( int , const EmscriptenMouseEvent * event , void * userData ) - > Int {
( [ ] ( int , const EmscriptenMouseEvent * event , void * userData ) - > Int {
MouseEvent e { * event } ;
MouseEvent e { * event } ;
static_cast < EmscriptenApplication * > ( userData ) - > mousePressEvent ( e ) ;
static_cast < EmscriptenApplication * > ( userData ) - > mousePressEvent ( e ) ;
return e . isAccepted ( ) ;
return e . isAccepted ( ) ;
} ) ) ;
} ) ) ;
emscripten_set_mouseup_callback ( " #canvas " , this , false ,
emscripten_set_mouseup_callback ( _canvasTarget . data ( ) , this , false ,
( [ ] ( int , const EmscriptenMouseEvent * event , void * userData ) - > Int {
( [ ] ( int , const EmscriptenMouseEvent * event , void * userData ) - > Int {
MouseEvent e { * event } ;
MouseEvent e { * event } ;
static_cast < EmscriptenApplication * > ( userData ) - > mouseReleaseEvent ( e ) ;
static_cast < EmscriptenApplication * > ( userData ) - > mouseReleaseEvent ( e ) ;
return e . isAccepted ( ) ;
return e . isAccepted ( ) ;
} ) ) ;
} ) ) ;
emscripten_set_mousemove_callback ( " #canvas " , this , false ,
emscripten_set_mousemove_callback ( _canvasTarget . data ( ) , this , false ,
( [ ] ( int , const EmscriptenMouseEvent * event , void * userData ) - > Int {
( [ ] ( int , const EmscriptenMouseEvent * event , void * userData ) - > Int {
auto & app = * static_cast < EmscriptenApplication * > ( userData ) ;
auto & app = * static_cast < EmscriptenApplication * > ( userData ) ;
/* With DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR, canvasX/Y is
/* With DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR, canvasX/Y is
@ -499,7 +540,7 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
return e . isAccepted ( ) ;
return e . isAccepted ( ) ;
} ) ) ;
} ) ) ;
emscripten_set_wheel_callback ( " #canvas " , this , false ,
emscripten_set_wheel_callback ( _canvasTarget . data ( ) , this , false ,
( [ ] ( int , const EmscriptenWheelEvent * event , void * userData ) - > Int {
( [ ] ( int , const EmscriptenWheelEvent * event , void * userData ) - > Int {
MouseScrollEvent e { * event } ;
MouseScrollEvent e { * event } ;
static_cast < EmscriptenApplication * > ( userData ) - > mouseScrollEvent ( e ) ;
static_cast < EmscriptenApplication * > ( userData ) - > mouseScrollEvent ( e ) ;
@ -516,31 +557,31 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
1.38 .27 depending on - s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR = 1
1.38 .27 depending on - s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR = 1
but we don ' t want to force this flag on the users so the behavior
but we don ' t want to force this flag on the users so the behavior
handles both . */
handles both . */
# ifdef EMSCRIPTEN_EVENT_TARGET_DOCUMENT
const char * keyboardListeningElement = reinterpret_cast < const char * > ( EM_ASM_INT ( {
char * const keyboardListeningElement = reinterpret_cast < char * > ( EM_ASM_INT ( {
var element = Module [ ' keyboardListeningElement ' ] | | document ;
var element = Module [ ' keyboardListeningElement ' ] | | document ;
if ( element = = = document ) return 1 ;
if ( element = = = document ) return 1 ; /* EMSCRIPTEN_EVENT_TARGET_DOCUMENT */
if ( element = = = window ) return 2 ;
if ( element = = = window ) return 2 ; /* EMSCRIPTEN_EVENT_TARGET_WINDOW */
if ( ' id ' in element )
if ( ' id ' in element )
return allocate ( intArrayFromString ( element . id ) , ' i8 ' , ALLOC_NORMAL ) ;
return allocate ( intArrayFromString ( element . id ) , ' i8 ' , ALLOC_NORMAL ) ;
return 0 ;
return 0 ;
} ) ) ;
} ) ) ;
# else
char * const keyboardListeningElement = reinterpret_cast < char * > ( EM_ASM_INT ( {
var element = Module [ ' keyboardListeningElement ' ] | | document ;
if ( element = = = document ) element = { id : ' # document ' } ;
if ( element = = = window ) element = { id : ' # window ' } ;
if ( ' id ' in element )
return allocate ( intArrayFromString ( element . id ) , ' i8 ' , ALLOC_NORMAL ) ;
return 0 ;
} ) ) ;
# endif
# pragma GCC diagnostic pop
# pragma GCC diagnostic pop
std : : string keyboardListeningElementString ;
if ( keyboardListeningElement = = EMSCRIPTEN_EVENT_TARGET_DOCUMENT ) {
keyboardListeningElement = _deprecatedTargetBehavior ? " #document " : keyboardListeningElement ;
} else if ( keyboardListeningElement = = EMSCRIPTEN_EVENT_TARGET_WINDOW ) {
keyboardListeningElement = _deprecatedTargetBehavior ? " #window " : keyboardListeningElement ;
} else if ( keyboardListeningElement ) {
if ( ! _deprecatedTargetBehavior )
keyboardListeningElementString = " # " ;
keyboardListeningElementString + = keyboardListeningElement ;
std : : free ( const_cast < char * > ( keyboardListeningElement ) ) ;
keyboardListeningElement = keyboardListeningElementString . data ( ) ;
}
/* Happens only if keyboardListeningElement was set, but did not have an
/* Happens only if keyboardListeningElement was set, but did not have an
` id ` attribute . Instead it should be either null or undefined , a DOM
` id ` attribute . Instead it should be either null or undefined , a DOM
element , ` window ` or ` document ` . */
element , ` window ` or ` document ` . */
@ -571,14 +612,6 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
static_cast < EmscriptenApplication * > ( userData ) - > keyReleaseEvent ( e ) ;
static_cast < EmscriptenApplication * > ( userData ) - > keyReleaseEvent ( e ) ;
return e . isAccepted ( ) ;
return e . isAccepted ( ) ;
} ) ) ;
} ) ) ;
# ifdef EMSCRIPTEN_EVENT_TARGET_DOCUMENT
if ( keyboardListeningElement ! = EMSCRIPTEN_EVENT_TARGET_DOCUMENT & &
keyboardListeningElement ! = EMSCRIPTEN_EVENT_TARGET_WINDOW )
# endif
{
std : : free ( keyboardListeningElement ) ;
}
}
}
void EmscriptenApplication : : setupAnimationFrame ( bool forceAnimationFrame ) {
void EmscriptenApplication : : setupAnimationFrame ( bool forceAnimationFrame ) {