diff --git a/src/library_html5.js b/src/library_html5.js index cd82337eb24cb..0e4866f81d3b7 100644 --- a/src/library_html5.js +++ b/src/library_html5.js @@ -4,30 +4,64 @@ * SPDX-License-Identifier: MIT */ +{{{ +globalThis.maxEventSize = Math.max( + C_STRUCTS.EmscriptenKeyboardEvent.__size__, + C_STRUCTS.EmscriptenMouseEvent.__size__, + C_STRUCTS.EmscriptenWheelEvent.__size__, + C_STRUCTS.EmscriptenUiEvent.__size__, + C_STRUCTS.EmscriptenFocusEvent.__size__, + C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__, + C_STRUCTS.EmscriptenDeviceMotionEvent.__size__, + C_STRUCTS.EmscriptenOrientationChangeEvent.__size__, + C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__, + C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__, + C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__, + C_STRUCTS.EmscriptenTouchEvent.__size__, + C_STRUCTS.EmscriptenGamepadEvent.__size__, + C_STRUCTS.EmscriptenBatteryEvent.__size__ +); +null; +}}} + var LibraryHTML5 = { + $JSEventCache__deps: ['malloc'], + $JSEventCache: { + memcpy(target, src, size) { + HEAP8.set(HEAP8.subarray(src, src + size), target); + }, + + // In addition to generic `eventStorage` we keep duplicates of certain + // events in this map of eventID to last generated event dat. This allows + // certain event types to simulate a polling interface. Specifically: + // - emscripten_get_mouse_status + // - emscripten_get_deviceorientation_status + // - emscripten_get_devicemotion_status + cachedEvents: {}, + cacheEvent(eventTypeId, eventData, size) { + JSEventCache.cachedEvents[eventTypeId] ??= _malloc(size); + JSEventCache.memcpy(JSEventCache.cachedEvents[eventTypeId], eventData, size); + }, + + getCachedEvent(eventId, target, size) { + if (!JSEventCache.cachedEvents[eventId]) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; + JSEventCache.memcpy(target, JSEventCache.cachedEvents[eventId], size); + return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + }, + }, + $JSEvents__deps: [ + 'malloc', #if PTHREADS '_emscripten_run_callback_on_thread', #endif ], $JSEvents: { - #if USE_CLOSURE_COMPILER - // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop. - batteryEvent: 0, - gamepadEvent: 0, - keyEvent: 0, - mouseEvent: 0, - wheelEvent: 0, - uiEvent: 0, - focusEvent: 0, - deviceOrientationEvent: 0, - orientationChangeEvent: 0, - deviceMotionEvent: 0, - fullscreenChangeEvent: 0, - pointerlockChangeEvent: 0, - visibilityChangeEvent: 0, - touchEvent: 0, + // Pointer to dynamically allocated event storage. This is always used + // as the eventData pointer in all callback and is allocated a + // `maxEventSize` bytes. + eventStorage: null, #endif /* We do not depend on the exact initial values of falsey member fields - these @@ -58,8 +92,21 @@ var LibraryHTML5 = { currentEventHandler: null, #endif */ - memcpy(target, src, size) { - HEAP8.set(HEAP8.subarray(src, src + size), target); + allocateEventStruct(targetThread) { +#if PTHREADS + // This allocated block is passed as satellite data to the proxied event + // handle, which then frees the data block when done. + if (targetThread) return _malloc({{{ maxEventSize }}}); +#endif + return JSEvents.eventStorage ??= _malloc({{{ maxEventSize }}}); + }, + + dispatchEvent(e, targetThread, callbackfunc, eventTypeId, eventData, userData) { +#if PTHREADS + if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, eventData, userData); + else +#endif + if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, eventData, userData)) e.preventDefault(); }, removeAllEventListeners() { @@ -256,23 +303,18 @@ var LibraryHTML5 = { }, }, - $registerKeyEventCallback__deps: ['$JSEvents', '$findEventTarget', '$stringToUTF8', 'malloc'], + $registerKeyEventCallback__deps: ['$JSEvents', '$findEventTarget', '$stringToUTF8'], $registerKeyEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.keyEvent ||= _malloc({{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}}); var keyEventHandlerFunc = (e) => { #if ASSERTIONS assert(e); #endif -#if PTHREADS - var keyEventData = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}}) : JSEvents.keyEvent; // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. -#else - var keyEventData = JSEvents.keyEvent; -#endif + var keyEventData = JSEvents.allocateEventStruct(targetThread); {{{ makeSetValue('keyEventData', C_STRUCTS.EmscriptenKeyboardEvent.timestamp, 'e.timeStamp', 'double') }}}; var idx = {{{ getHeapOffset('keyEventData', 'i32') }}}; @@ -291,11 +333,7 @@ var LibraryHTML5 = { stringToUTF8(e.char || '', keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.charValue }}}, {{{ cDefs.EM_HTML5_SHORT_STRING_LEN_BYTES }}}); stringToUTF8(e.locale || '', keyEventData + {{{ C_STRUCTS.EmscriptenKeyboardEvent.locale }}}, {{{ cDefs.EM_HTML5_SHORT_STRING_LEN_BYTES }}}); -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, keyEventData, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, keyEventData, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, keyEventData, userData); }; var eventHandler = { @@ -510,26 +548,20 @@ var LibraryHTML5 = { #endif }, - $registerMouseEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget', 'malloc'], + $registerMouseEventCallback__deps: ['$JSEvents', '$JSEventCache', '$fillMouseEventData', '$findEventTarget'], $registerMouseEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.mouseEvent ||= _malloc({{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); target = findEventTarget(target); var mouseEventHandlerFunc = (e = event) => { - // TODO: Make this access thread safe, or this could update live while app is reading it. - fillMouseEventData(JSEvents.mouseEvent, e, target); - -#if PTHREADS - if (targetThread) { - var mouseEventData = _malloc({{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. - fillMouseEventData(mouseEventData, e, target); - __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, mouseEventData, userData); - } else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); + var mouseEvent = JSEvents.allocateEventStruct(targetThread); + fillMouseEventData(mouseEvent, e, target); + // Cache the last event so that it can be polled via + // emscripten_get_deviceorientation_status + JSEventCache.cacheEvent({{{ cDefs.EMSCRIPTEN_EVENT_MOUSEDOWN }}}, mouseEvent, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, mouseEvent, userData); }; var eventHandler = { @@ -591,55 +623,42 @@ var LibraryHTML5 = { registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_MOUSEOUT }}}, "mouseout", targetThread), emscripten_get_mouse_status__proxy: 'sync', - emscripten_get_mouse_status__deps: ['$JSEvents'], + emscripten_get_mouse_status__deps: ['$JSEventCache'], emscripten_get_mouse_status: (mouseState) => { - if (!JSEvents.mouseEvent) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; // HTML5 does not really have a polling API for mouse events, so implement one manually by // returning the data from the most recently received event. This requires that user has registered // at least some no-op function as an event handler to any of the mouse function. - JSEvents.memcpy(mouseState, JSEvents.mouseEvent, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); - return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + return JSEventCache.getCachedEvent({{{ cDefs.EMSCRIPTEN_EVENT_MOUSEDOWN }}}, mouseState, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}); }, - $registerWheelEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget', 'malloc'], + $registerWheelEventCallback__deps: ['$JSEvents', '$fillMouseEventData', '$findEventTarget'], $registerWheelEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.wheelEvent ||= _malloc({{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}}); // The DOM Level 3 events spec event 'wheel' var wheelHandlerFunc = (e = event) => { -#if PTHREADS - var wheelEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}}) : JSEvents.wheelEvent; // This allocated block is passed as satellite data to the proxied function call, so the call frees up the data block when done. -#else - var wheelEvent = JSEvents.wheelEvent; -#endif + var wheelEvent = JSEvents.allocateEventStruct(targetThread); fillMouseEventData(wheelEvent, e, target); {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["deltaX"]', 'double') }}}; {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e["deltaY"]', 'double') }}}; {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e["deltaZ"]', 'double') }}}; {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e["deltaMode"]', 'i32') }}}; -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, wheelEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, wheelEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, wheelEvent, userData); }; #if MIN_SAFARI_VERSION < 60100 // Browsers that do not support https://caniuse.com/#feat=mdn-api_wheelevent // The 'mousewheel' event as implemented in Safari 6.0.5 var mouseWheelHandlerFunc = (e = event) => { - fillMouseEventData(JSEvents.wheelEvent, e, target); - {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["wheelDeltaX"] || 0', 'double') }}}; + var wheelEvent = JSEvents.allocateEventStruct(targetThread); + fillMouseEventData(wheelEvent, e, target); + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e["wheelDeltaX"] || 0', 'double') }}}; /* 1. Invert to unify direction with the DOM Level 3 wheel event. 2. MSIE does not provide wheelDeltaY, so wheelDelta is used as a fallback. */ var wheelDeltaY = -(e["wheelDeltaY"] || e["wheelDelta"]) - {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'wheelDeltaY', 'double') }}}; - {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, '0 /* Not available */', 'double') }}}; - {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, '0 /* DOM_DELTA_PIXEL */', 'i32') }}}; - var shouldCancel = {{{ makeDynCall('iipp', 'callbackfunc') }}}( eventTypeId, JSEvents.wheelEvent, userData); - if (shouldCancel) { - e.preventDefault(); - } + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'wheelDeltaY', 'double') }}}; + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, '0 /* Not available */', 'double') }}}; + {{{ makeSetValue('wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, '0 /* DOM_DELTA_PIXEL */', 'i32') }}}; + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, wheelEvent, userData); }; #endif @@ -676,12 +695,11 @@ var LibraryHTML5 = { } }, - $registerUiEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc'], + $registerUiEventCallback__deps: ['$JSEvents', '$findEventTarget'], $registerUiEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.uiEvent ||= _malloc({{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}}); #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR target = findEventTarget(target); @@ -707,11 +725,7 @@ var LibraryHTML5 = { // During a page unload 'body' can be null, with "Cannot read property 'clientWidth' of null" being thrown return; } -#if PTHREADS - var uiEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}}) : JSEvents.uiEvent; -#else - var uiEvent = JSEvents.uiEvent; -#endif + var uiEvent = JSEvents.allocateEventStruct(targetThread); {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.detail, '0', 'i32') }}}; // always zero for resize and scroll {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientWidth, 'b.clientWidth', 'i32') }}}; {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientHeight, 'b.clientHeight', 'i32') }}}; @@ -721,11 +735,7 @@ var LibraryHTML5 = { {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterHeight, 'outerHeight', 'i32') }}}; {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollTop, 'pageXOffset | 0', 'i32') }}}; // scroll offsets are float {{{ makeSetValue('uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollLeft, 'pageYOffset | 0', 'i32') }}}; -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, uiEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, uiEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, uiEvent, userData); }; var eventHandler = { @@ -748,30 +758,21 @@ var LibraryHTML5 = { emscripten_set_scroll_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_SCROLL }}}, "scroll", targetThread), - $registerFocusEventCallback__deps: ['$JSEvents', '$findEventTarget', 'malloc', '$stringToUTF8'], + $registerFocusEventCallback__deps: ['$JSEvents', '$findEventTarget', '$stringToUTF8'], $registerFocusEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.focusEvent ||= _malloc({{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}}); var focusEventHandlerFunc = (e = event) => { var nodeName = JSEvents.getNodeNameForTarget(e.target); var id = e.target.id ? e.target.id : ''; -#if PTHREADS - var focusEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}}) : JSEvents.focusEvent; -#else - var focusEvent = JSEvents.focusEvent; -#endif + var focusEvent = JSEvents.allocateEventStruct(targetThread); stringToUTF8(nodeName, focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.nodeName }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); stringToUTF8(id, focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.id }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, focusEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, focusEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, focusEvent, userData); }; var eventHandler = { @@ -804,7 +805,6 @@ var LibraryHTML5 = { emscripten_set_focusout_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) => registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_FOCUSOUT }}}, "focusout", targetThread), - $fillDeviceOrientationEventData__deps: ['$JSEvents'], $fillDeviceOrientationEventData: (eventStruct, e, target) => { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.alpha, 'e.alpha', 'double') }}}; {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.beta, 'e.beta', 'double') }}}; @@ -812,24 +812,21 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceOrientationEvent.absolute, 'e.absolute', 'i8') }}}; }, - $registerDeviceOrientationEventCallback__deps: ['$JSEvents', '$fillDeviceOrientationEventData', '$findEventTarget'], - $registerDeviceOrientationEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { + $registerDeviceOrientationEventCallback__deps: ['$JSEvents', '$JSEventCache', '$fillDeviceOrientationEventData', '$findEventTarget'], + $registerDeviceOrientationEventCallback: (target, userData, useCapture, callbackfunc, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.deviceOrientationEvent ||= _malloc({{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); + var eventTypeId = {{{ cDefs.EMSCRIPTEN_EVENT_DEVICEORIENTATION }}}; + var eventTypeString = "deviceorientation"; var deviceOrientationEventHandlerFunc = (e = event) => { - fillDeviceOrientationEventData(JSEvents.deviceOrientationEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_deviceorientation_status() - -#if PTHREADS - if (targetThread) { - var deviceOrientationEvent = _malloc({{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); - fillDeviceOrientationEventData(deviceOrientationEvent, e, target); - __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, deviceOrientationEvent, userData); - } else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.deviceOrientationEvent, userData)) e.preventDefault(); + var deviceOrientationEvent = JSEvents.allocateEventStruct(targetThread); + fillDeviceOrientationEventData(deviceOrientationEvent, e, target); + // Cache the last event so that it can be polled via + // emscripten_get_deviceorientation_status + JSEventCache.cacheEvent(eventTypeId, deviceOrientationEvent, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, deviceOrientationEvent, userData); }; var eventHandler = { @@ -845,18 +842,16 @@ var LibraryHTML5 = { emscripten_set_deviceorientation_callback_on_thread__proxy: 'sync', emscripten_set_deviceorientation_callback_on_thread__deps: ['$registerDeviceOrientationEventCallback'], emscripten_set_deviceorientation_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { - return registerDeviceOrientationEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_DEVICEORIENTATION }}}, "deviceorientation", targetThread); + return registerDeviceOrientationEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, targetThread); }, emscripten_get_deviceorientation_status__proxy: 'sync', - emscripten_get_deviceorientation_status__deps: ['$JSEvents', '$registerDeviceOrientationEventCallback'], + emscripten_get_deviceorientation_status__deps: ['$JSEventCache'], emscripten_get_deviceorientation_status: (orientationState) => { - if (!JSEvents.deviceOrientationEvent) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; // HTML5 does not really have a polling API for device orientation events, so implement one manually by // returning the data from the most recently received event. This requires that user has registered // at least some no-op function as an event handler. - JSEvents.memcpy(orientationState, JSEvents.deviceOrientationEvent, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); - return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + return JSEventCache.getCachedEvent({{{ cDefs.EMSCRIPTEN_EVENT_DEVICEORIENTATION }}}, orientationState, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}); }, $fillDeviceMotionEventData__deps: ['$JSEvents'], @@ -882,24 +877,22 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateGamma, 'rr["gamma"]', 'double') }}}; }, - $registerDeviceMotionEventCallback__deps: ['$JSEvents', '$fillDeviceMotionEventData', '$findEventTarget', 'malloc'], - $registerDeviceMotionEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { + $registerDeviceMotionEventCallback__deps: ['$JSEvents', '$JSEventCache', '$fillDeviceMotionEventData', '$findEventTarget'], + $registerDeviceMotionEventCallback: (userData, useCapture, callbackfunc, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.deviceMotionEvent ||= _malloc({{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); + var target = {{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}; + var eventTypeId = {{{ cDefs.EMSCRIPTEN_EVENT_DEVICEMOTION }}}; + var eventTypeString = "devicemotion"; var deviceMotionEventHandlerFunc = (e = event) => { - fillDeviceMotionEventData(JSEvents.deviceMotionEvent, e, target); // TODO: Thread-safety with respect to emscripten_get_devicemotion_status() - -#if PTHREADS - if (targetThread) { - var deviceMotionEvent = _malloc({{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); - fillDeviceMotionEventData(deviceMotionEvent, e, target); - __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, deviceMotionEvent, userData); - } else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, JSEvents.deviceMotionEvent, userData)) e.preventDefault(); + var deviceMotionEvent = JSEvents.allocateEventStruct(targetThread); + fillDeviceMotionEventData(deviceMotionEvent, e, target); + // Cache the last event so that it can be polled via + // emscripten_get_deviceorientation_status + JSEventCache.cacheEvent(eventTypeId, deviceMotionEvent, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, deviceMotionEvent, userData); }; var eventHandler = { @@ -915,18 +908,16 @@ var LibraryHTML5 = { emscripten_set_devicemotion_callback_on_thread__proxy: 'sync', emscripten_set_devicemotion_callback_on_thread__deps: ['$registerDeviceMotionEventCallback'], emscripten_set_devicemotion_callback_on_thread: (userData, useCapture, callbackfunc, targetThread) => { - return registerDeviceMotionEventCallback({{{ cDefs.EMSCRIPTEN_EVENT_TARGET_WINDOW }}}, userData, useCapture, callbackfunc, {{{ cDefs.EMSCRIPTEN_EVENT_DEVICEMOTION }}}, "devicemotion", targetThread); + return registerDeviceMotionEventCallback(userData, useCapture, callbackfunc, targetThread); }, emscripten_get_devicemotion_status__proxy: 'sync', - emscripten_get_devicemotion_status__deps: ['$JSEvents'], + emscripten_get_devicemotion_status__deps: ['$JSEventCache'], emscripten_get_devicemotion_status: (motionState) => { - if (!JSEvents.deviceMotionEvent) return {{{ cDefs.EMSCRIPTEN_RESULT_NO_DATA }}}; // HTML5 does not really have a polling API for device motion events, so implement one manually by // returning the data from the most recently received event. This requires that user has registered // at least some no-op function as an event handler. - JSEvents.memcpy(motionState, JSEvents.deviceMotionEvent, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); - return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; + return JSEventCache.getCachedEvent({{{ cDefs.EMSCRIPTEN_EVENT_DEVICEMOTION }}}, motionState, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}); }, $screenOrientation: () => { @@ -965,27 +956,16 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationAngle, 'orientationAngle', 'i32') }}}; }, - $registerOrientationChangeEventCallback__deps: ['$JSEvents', '$fillOrientationChangeEventData', '$findEventTarget', 'malloc'], + $registerOrientationChangeEventCallback__deps: ['$JSEvents', '$fillOrientationChangeEventData', '$findEventTarget'], $registerOrientationChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.orientationChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}}); var orientationChangeEventHandlerFunc = (e = event) => { -#if PTHREADS - var orientationChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}}) : JSEvents.orientationChangeEvent; -#else - var orientationChangeEvent = JSEvents.orientationChangeEvent; -#endif - + var orientationChangeEvent = JSEvents.allocateEventStruct(targetThread); fillOrientationChangeEventData(orientationChangeEvent); - -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, orientationChangeEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, orientationChangeEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, orientationChangeEvent, userData); }; var eventHandler = { @@ -1077,27 +1057,16 @@ var LibraryHTML5 = { } }, - $registerFullscreenChangeEventCallback__deps: ['$JSEvents', '$fillFullscreenChangeEventData', '$findEventTarget', 'malloc'], + $registerFullscreenChangeEventCallback__deps: ['$JSEvents', '$fillFullscreenChangeEventData', '$findEventTarget'], $registerFullscreenChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.fullscreenChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}}); var fullscreenChangeEventhandlerFunc = (e = event) => { -#if PTHREADS - var fullscreenChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}}) : JSEvents.fullscreenChangeEvent; -#else - var fullscreenChangeEvent = JSEvents.fullscreenChangeEvent; -#endif - + var fullscreenChangeEvent = JSEvents.allocateEventStruct(targetThread); fillFullscreenChangeEventData(fullscreenChangeEvent); - -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, fullscreenChangeEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, fullscreenChangeEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, fullscreenChangeEvent, userData); }; var eventHandler = { @@ -1607,26 +1576,16 @@ var LibraryHTML5 = { stringToUTF8(id, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.id }}}, {{{ cDefs.EM_HTML5_LONG_STRING_LEN_BYTES }}}); }, - $registerPointerlockChangeEventCallback__deps: ['$JSEvents', '$fillPointerlockChangeEventData', '$findEventTarget', 'malloc'], + $registerPointerlockChangeEventCallback__deps: ['$JSEvents', '$fillPointerlockChangeEventData', '$findEventTarget'], $registerPointerlockChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.pointerlockChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}}); var pointerlockChangeEventHandlerFunc = (e = event) => { -#if PTHREADS - var pointerlockChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}}) : JSEvents.pointerlockChangeEvent; -#else - var pointerlockChangeEvent = JSEvents.pointerlockChangeEvent; -#endif + var pointerlockChangeEvent = JSEvents.allocateEventStruct(targetThread); fillPointerlockChangeEventData(pointerlockChangeEvent); - -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, pointerlockChangeEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, pointerlockChangeEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, pointerlockChangeEvent, userData); }; var eventHandler = { @@ -1667,11 +1626,7 @@ var LibraryHTML5 = { #endif var pointerlockErrorEventHandlerFunc = (e = event) => { -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, 0, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, 0, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, 0, userData); }; var eventHandler = { @@ -1836,27 +1791,16 @@ var LibraryHTML5 = { {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}}; }, - $registerVisibilityChangeEventCallback__deps: ['$JSEvents', '$fillVisibilityChangeEventData', '$findEventTarget', 'malloc'], + $registerVisibilityChangeEventCallback__deps: ['$JSEvents', '$fillVisibilityChangeEventData', '$findEventTarget'], $registerVisibilityChangeEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.visibilityChangeEvent ||= _malloc({{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}}); var visibilityChangeEventHandlerFunc = (e = event) => { -#if PTHREADS - var visibilityChangeEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}}) : JSEvents.visibilityChangeEvent; -#else - var visibilityChangeEvent = JSEvents.visibilityChangeEvent; -#endif - + var visibilityChangeEvent = JSEvents.allocateEventStruct(targetThread); fillVisibilityChangeEventData(visibilityChangeEvent); - -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, visibilityChangeEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, visibilityChangeEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, visibilityChangeEvent, userData); }; var eventHandler = { @@ -1890,12 +1834,11 @@ var LibraryHTML5 = { return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, - $registerTouchEventCallback__deps: ['$JSEvents', '$findEventTarget', '$getBoundingClientRect', 'malloc'], + $registerTouchEventCallback__deps: ['$JSEvents', '$findEventTarget', '$getBoundingClientRect'], $registerTouchEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.touchEvent ||= _malloc({{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}}); target = findEventTarget(target); @@ -1924,11 +1867,7 @@ var LibraryHTML5 = { touches[t.identifier].onTarget = 1; } -#if PTHREADS - var touchEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}}) : JSEvents.touchEvent; -#else - var touchEvent = JSEvents.touchEvent; -#endif + var touchEvent = JSEvents.allocateEventStruct(targetThread); {{{ makeSetValue('touchEvent', C_STRUCTS.EmscriptenTouchEvent.timestamp, 'e.timeStamp', 'double') }}}; HEAP8[touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.ctrlKey }}}] = e.ctrlKey; HEAP8[touchEvent + {{{ C_STRUCTS.EmscriptenTouchEvent.shiftKey }}}] = e.shiftKey; @@ -1966,11 +1905,7 @@ var LibraryHTML5 = { } {{{ makeSetValue('touchEvent', C_STRUCTS.EmscriptenTouchEvent.numTouches, 'numTouches', 'i32') }}}; -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, touchEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, touchEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, touchEvent, userData); }; var eventHandler = { @@ -2038,26 +1973,16 @@ var LibraryHTML5 = { stringToUTF8(e.mapping, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.mapping }}}, {{{ cDefs.EM_HTML5_MEDIUM_STRING_LEN_BYTES }}}); }, - $registerGamepadEventCallback__deps: ['$JSEvents', '$fillGamepadEventData', '$findEventTarget', 'malloc'], + $registerGamepadEventCallback__deps: ['$JSEvents', '$fillGamepadEventData', '$findEventTarget'], $registerGamepadEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.gamepadEvent ||= _malloc({{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}}); var gamepadEventHandlerFunc = (e = event) => { -#if PTHREADS - var gamepadEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}}) : JSEvents.gamepadEvent; -#else - var gamepadEvent = JSEvents.gamepadEvent; -#endif + var gamepadEvent = JSEvents.allocateEventStruct(targetThread); fillGamepadEventData(gamepadEvent, e["gamepad"]); - -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, gamepadEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, gamepadEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, gamepadEvent, userData); }; var eventHandler = { @@ -2178,26 +2103,16 @@ var LibraryHTML5 = { $battery: () => navigator.battery || navigator.mozBattery || navigator.webkitBattery, - $registerBatteryEventCallback__deps: ['$JSEvents', '$fillBatteryEventData', '$battery', '$findEventTarget', 'malloc'], + $registerBatteryEventCallback__deps: ['$JSEvents', '$fillBatteryEventData', '$battery', '$findEventTarget'], $registerBatteryEventCallback: (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { #if PTHREADS targetThread = JSEvents.getTargetThreadForEventCallback(targetThread); #endif - JSEvents.batteryEvent ||= _malloc({{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}}); var batteryEventHandlerFunc = (e = event) => { -#if PTHREADS - var batteryEvent = targetThread ? _malloc({{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}}) : JSEvents.batteryEvent; -#else - var batteryEvent = JSEvents.batteryEvent; -#endif + var batteryEvent = JSEvents.allocateEventStruct(targetThread); fillBatteryEventData(batteryEvent, battery()); - -#if PTHREADS - if (targetThread) __emscripten_run_callback_on_thread(targetThread, callbackfunc, eventTypeId, batteryEvent, userData); - else -#endif - if ({{{ makeDynCall('iipp', 'callbackfunc') }}}(eventTypeId, batteryEvent, userData)) e.preventDefault(); + JSEvents.dispatchEvent(e, targetThread, callbackfunc, eventTypeId, batteryEvent, userData); }; var eventHandler = { diff --git a/test/code_size/hello_webgl2_wasm.json b/test/code_size/hello_webgl2_wasm.json index 6a80b58ccd57a..a642dcdfc7343 100644 --- a/test/code_size/hello_webgl2_wasm.json +++ b/test/code_size/hello_webgl2_wasm.json @@ -3,8 +3,8 @@ "a.html.gz": 328, "a.js": 4531, "a.js.gz": 2312, - "a.wasm": 10402, - "a.wasm.gz": 6704, - "total": 15387, - "total_gz": 9344 + "a.wasm": 10421, + "a.wasm.gz": 6697, + "total": 15406, + "total_gz": 9337 } diff --git a/test/code_size/hello_webgl2_wasm2js.json b/test/code_size/hello_webgl2_wasm2js.json index 3d8c851127bc8..977bc19609309 100644 --- a/test/code_size/hello_webgl2_wasm2js.json +++ b/test/code_size/hello_webgl2_wasm2js.json @@ -1,8 +1,8 @@ { "a.html": 346, "a.html.gz": 262, - "a.js": 22199, - "a.js.gz": 11579, - "total": 22545, - "total_gz": 11841 + "a.js": 22320, + "a.js.gz": 11639, + "total": 22666, + "total_gz": 11901 } diff --git a/test/code_size/hello_webgl_wasm.json b/test/code_size/hello_webgl_wasm.json index 78cc66760308e..e491208981210 100644 --- a/test/code_size/hello_webgl_wasm.json +++ b/test/code_size/hello_webgl_wasm.json @@ -3,8 +3,8 @@ "a.html.gz": 328, "a.js": 4069, "a.js.gz": 2158, - "a.wasm": 10402, - "a.wasm.gz": 6704, - "total": 14925, - "total_gz": 9190 + "a.wasm": 10421, + "a.wasm.gz": 6697, + "total": 14944, + "total_gz": 9183 } diff --git a/test/code_size/hello_webgl_wasm2js.json b/test/code_size/hello_webgl_wasm2js.json index 1320f0d8bddcf..7ee5053d955b4 100644 --- a/test/code_size/hello_webgl_wasm2js.json +++ b/test/code_size/hello_webgl_wasm2js.json @@ -1,8 +1,8 @@ { "a.html": 346, "a.html.gz": 262, - "a.js": 21725, - "a.js.gz": 11413, - "total": 22071, - "total_gz": 11675 + "a.js": 21846, + "a.js.gz": 11479, + "total": 22192, + "total_gz": 11741 } diff --git a/test/test_html5_mouse.c b/test/test_html5_mouse.c index 056e1dc6a64d1..7fa1588dd919b 100644 --- a/test/test_html5_mouse.c +++ b/test/test_html5_mouse.c @@ -5,22 +5,12 @@ * found in the LICENSE file. */ +#include #include #include #include #include -void report_result(int result) { - if (result == 0) { - printf("Test successful!\n"); - } else { - printf("Test failed!\n"); - } -#ifdef REPORT_RESULT - REPORT_RESULT(result); -#endif -} - static inline const char *emscripten_event_type_to_string(int eventType) { const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize", "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange", @@ -62,7 +52,12 @@ void instruction() { if (!gotMouseMove) { printf("Please move the mouse on the canvas.\n"); return; } if (!gotWheel) { printf("Please scroll the mouse wheel.\n"); return; } - if (gotClick && gotMouseDown && gotMouseUp && gotDblClick && gotMouseMove && gotWheel) report_result(0); + if (gotClick && gotMouseDown && gotMouseUp && gotDblClick && gotMouseMove && gotWheel) { + printf("Test successful!\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(0); +#endif + } } bool mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData) { @@ -82,7 +77,7 @@ bool mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData if (eventType == EMSCRIPTEN_EVENT_CLICK && e->screenX == -500000) { printf("ERROR! Received an event to a callback that should have been unregistered!\n"); gotClick = 0; - report_result(1); + assert(false && "Received an event to a callback that should have been unregistered"); } instruction(); @@ -141,7 +136,7 @@ int main() { if (mouseEvent.screenX != 123 || mouseEvent.screenY != 456 || mouseEvent.clientX != 123 || mouseEvent.clientY != 456) { printf("ERROR! Incorrect mouse status\n"); - report_result(1); + assert(false && "Incorrect mouse status"); } // Test that unregistering a callback works. Clicks should no longer be received.