summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--src/jsifier.js3
-rw-r--r--src/library_gl.js6
-rw-r--r--src/library_html5.js1257
-rw-r--r--src/library_openal.js306
-rw-r--r--src/modules.js2
-rw-r--r--src/relooper/Relooper.cpp26
-rw-r--r--src/shell.html43
-rw-r--r--src/struct_info.json185
-rw-r--r--system/include/emscripten/emscripten.h7
-rw-r--r--system/include/emscripten/html5.h628
-rw-r--r--tests/box2d/Benchmark.cpp79
-rw-r--r--tests/sqlite/benchmark.c40
-rw-r--r--tests/test_browser.py2
-rw-r--r--tests/test_core.py43
-rw-r--r--tests/test_float_literals.cpp92
-rw-r--r--tests/test_float_literals.out22
-rw-r--r--tests/test_html5.c474
-rw-r--r--tests/test_other.py2
-rw-r--r--tools/js-optimizer.js205
-rw-r--r--tools/js_optimizer.py6
-rw-r--r--tools/shared.py2
-rw-r--r--tools/test-js-optimizer-asm-regs-harder-output.js129
-rw-r--r--tools/test-js-optimizer-asm-regs-harder.js150
24 files changed, 3519 insertions, 191 deletions
diff --git a/AUTHORS b/AUTHORS
index 792fb523..2c292dde 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -119,3 +119,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Emerson José Silveira da Costa <emerson.costa@gmail.com>
* Jari Vetoniemi <mailroxas@gmail.com>
* Sindre Sorhus <sindresorhus@gmail.com>
+* James S Urquhart <jamesu@gmail.com>
diff --git a/src/jsifier.js b/src/jsifier.js
index 726a5eda..f4819584 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1844,7 +1844,7 @@ function JSify(data, functionsOnly) {
// rest of the output that we started to print out earlier (see comment on the
// "Final shape that will be created").
if (PRECISE_I64_MATH && Types.preciseI64MathUsed) {
- if (!INCLUDE_FULL_LIBRARY) {
+ if (!INCLUDE_FULL_LIBRARY && !SIDE_MODULE) {
// first row are utilities called from generated code, second are needed from fastLong
['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr',
'llvm_ctlz_i32', 'llvm_cttz_i32'].forEach(function(func) {
@@ -1866,6 +1866,7 @@ function JSify(data, functionsOnly) {
}
});
}
+ // these may be duplicated in side modules and the main module without issue
print(read('fastLong.js'));
print('// EMSCRIPTEN_END_FUNCS\n');
print(read('long.js'));
diff --git a/src/library_gl.js b/src/library_gl.js
index 3055309b..0a30292a 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -957,7 +957,11 @@ var LibraryGL = {
usage = 0x88E8; // GL_DYNAMIC_DRAW
break;
}
- GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
+ if (!data) {
+ GLctx.bufferData(target, size, usage);
+ } else {
+ GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
+ }
},
glBufferSubData__sig: 'viiii',
diff --git a/src/library_html5.js b/src/library_html5.js
new file mode 100644
index 00000000..703f9a74
--- /dev/null
+++ b/src/library_html5.js
@@ -0,0 +1,1257 @@
+var LibraryJSEvents = {
+ $JSEvents: {
+ // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop.
+ keyEvent: 0,
+ mouseEvent: 0,
+ wheelEvent: 0,
+ uiEvent: 0,
+ focusEvent: 0,
+ deviceOrientationEvent: 0,
+ deviceMotionEvent: 0,
+ fullscreenChangeEvent: 0,
+ pointerlockChangeEvent: 0,
+ visibilityChangeEvent: 0,
+ touchEvent: 0,
+
+ // When the C runtime exits via exit(), we unregister all event handlers added by this library to be nice and clean.
+ // Track in this field whether we have yet registered that __ATEXIT__ handler.
+ removeEventListenersRegistered: false,
+
+ registerRemoveEventListeners: function() {
+ if (!JSEvents.removeEventListenersRegistered) {
+ __ATEXIT__.push({ func: function() {
+ for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) {
+ JSEvents._removeHandler(i);
+ }
+ } });
+ JSEvents.removeEventListenersRegistered = true;
+ }
+ },
+
+ findEventTarget: function(target) {
+ if (target) {
+ if (typeof target == "number") {
+ target = Pointer_stringify(target);
+ }
+ if (target == '#window') return window;
+ else if (target == '#document') return document;
+ else if (target == '#screen') return window.screen;
+ else if (target == '#canvas') return Module['canvas'];
+
+ if (typeof target == 'string') return document.getElementById(target);
+ else return target;
+ } else {
+ // The sensible target varies between events, but use window as the default
+ // since DOM events mostly can default to that. Specific callback registrations
+ // override their own defaults.
+ return window;
+ }
+ },
+
+ deferredCalls: [],
+
+ // Queues the given function call to occur the next time we enter an event handler.
+ // Existing implementations of pointerlock apis have required that
+ // the target element is active in fullscreen mode first. Thefefore give
+ // fullscreen mode request a precedence of 1 and pointer lock a precedence of 2
+ // and sort by that to always request fullscreen before pointer lock.
+ deferCall: function(targetFunction, precedence, argsList) {
+ function arraysHaveEqualContent(arrA, arrB) {
+ if (arrA.length != arrB.length) return false;
+
+ for(var i in arrA) {
+ if (arrA[i] != arrB[i]) return false;
+ }
+ return true;
+ }
+ // Test if the given call was already queued, and if so, don't add it again.
+ for(var i in JSEvents.deferredCalls) {
+ var call = JSEvents.deferredCalls[i];
+ if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) {
+ return;
+ }
+ }
+ JSEvents.deferredCalls.push({
+ targetFunction: targetFunction,
+ precedence: precedence,
+ argsList: argsList
+ });
+
+ JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; });
+ },
+
+ // Erases all deferred calls to the given target function from the queue list.
+ removeDeferredCalls: function(targetFunction) {
+ for(var i = 0; i < JSEvents.deferredCalls.length; ++i) {
+ if (JSEvents.deferredCalls[i].targetFunction == targetFunction) {
+ JSEvents.deferredCalls.splice(i, 1);
+ --i;
+ }
+ }
+ },
+
+ canPerformEventHandlerRequests: function() {
+ return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls;
+ },
+
+ runDeferredCalls: function() {
+ if (!JSEvents.canPerformEventHandlerRequests()) {
+ return;
+ }
+ for(var i = 0; i < JSEvents.deferredCalls.length; ++i) {
+ var call = JSEvents.deferredCalls[i];
+ JSEvents.deferredCalls.splice(i, 1);
+ --i;
+ call.targetFunction.apply(this, call.argsList);
+ }
+ },
+
+ // If positive, we are currently executing in a JS event handler.
+ inEventHandler: 0,
+ // If we are in an event handler, specifies the event handler object from the eventHandlers array that is currently running.
+ currentEventHandler: null,
+
+ // Stores objects representing each currently registered JS event handler.
+ eventHandlers: [],
+
+ _removeHandler: function(i) {
+ JSEvents.eventHandlers[i].target.removeEventListener(JSEvents.eventHandlers[i].eventTypeString, JSEvents.eventHandlers[i].handlerFunc, true);
+ JSEvents.eventHandlers.splice(i, 1);
+ },
+
+ registerOrRemoveHandler: function(eventHandler) {
+ var jsEventHandler = function jsEventHandler(event) {
+ // Increment nesting count for the event handler.
+ ++JSEvents.inEventHandler;
+ JSEvents.currentEventHandler = eventHandler;
+ // Process any old deferred calls the user has placed.
+ JSEvents.runDeferredCalls();
+ // Process the actual event, calls back to user C code handler.
+ eventHandler.handlerFunc(event);
+ // Process any new deferred calls that were placed right now from this event handler.
+ JSEvents.runDeferredCalls();
+ // Out of event handler - restore nesting count.
+ --JSEvents.inEventHandler;
+ }
+
+ if (eventHandler.callbackfunc) {
+ eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture);
+ JSEvents.eventHandlers.push(eventHandler);
+ JSEvents.registerRemoveEventListeners();
+ } else {
+ for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {
+ if (JSEvents.eventHandlers[i].target == eventHandler.target
+ && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) {
+ JSEvents._removeHandler(i--);
+ }
+ }
+ }
+ },
+
+ registerKeyEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.keyEvent) {
+ JSEvents.keyEvent = _malloc( {{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}} );
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+ writeStringToMemory(e.key ? e.key : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.key }}} );
+ writeStringToMemory(e.code ? e.code : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.code }}} );
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.location, 'e.location', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.ctrlKey, 'e.ctrlKey', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.shiftKey, 'e.shiftKey', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.altKey, 'e.altKey', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.metaKey, 'e.metaKey', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.repeat, 'e.repeat', 'i32') }}}
+ writeStringToMemory(e.locale ? e.locale : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.locale }}} );
+ writeStringToMemory(e.char ? e.char : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.charValue }}} );
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.charCode, 'e.charCode', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.keyCode, 'e.keyCode', 'i32') }}}
+ {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.which, 'e.which', 'i32') }}}
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.keyEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var isInternetExplorer = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0);
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: isInternetExplorer ? false : true, // MSIE doesn't allow fullscreen and pointerlock requests from key handlers, others do.
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ fillMouseEventData: function(eventStruct, e) {
+ var rect = Module['canvas'].getBoundingClientRect();
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.timestamp, 'JSEvents.tick()', 'double') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.screenX, 'e.screenX', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.screenY, 'e.screenY', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.clientX, 'e.clientX', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.clientY, 'e.clientY', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.ctrlKey, 'e.ctrlKey', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.shiftKey, 'e.shiftKey', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.altKey, 'e.altKey', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.metaKey, 'e.metaKey', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.button, 'e.button', 'i16') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.buttons, 'e.buttons', 'i16') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementX, 'e.movementX', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementY, 'e.movementY', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasX, 'e.clientX - rect.left', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasY, 'e.clientY - rect.top', 'i32') }}}
+ },
+
+ registerMouseEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.mouseEvent) {
+ JSEvents.mouseEvent = _malloc( {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}} );
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+ JSEvents.fillMouseEventData(JSEvents.mouseEvent, e);
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.mouseEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: eventTypeString != 'mousemove', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them!
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ registerWheelEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.wheelEvent) {
+ JSEvents.wheelEvent = _malloc( {{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}} );
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+ JSEvents.fillMouseEventData(JSEvents.wheelEvent, e);
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e.deltaX', 'double') }}}
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e.deltaY', 'double') }}}
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e.deltaZ', 'double') }}}
+ {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e.deltaMode', 'i32') }}}
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.wheelEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: true,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ pageScrollPos: function() {
+ if (window.pageXOffset > 0 || window.pageYOffset > 0) {
+ return [window.pageXOffset, window.pageYOffset];
+ }
+ if (typeof document.documentElement.scrollLeft !== 'undefined' || typeof document.documentElement.scrollTop !== 'undefined') {
+ return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
+ }
+ return [document.body.scrollLeft|0, document.body.scrollTop|0];
+ },
+
+ registerUiEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.uiEvent) {
+ JSEvents.uiEvent = _malloc( {{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}} );
+ }
+
+ if (eventTypeString == "scroll" && !target) {
+ target = document; // By default read scroll events on document rather than window.
+ } else {
+ target = JSEvents.findEventTarget(target);
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+ if (e.target != target) {
+ // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that
+ // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log
+ // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print,
+ // causing a new scroll, etc..
+ return;
+ }
+ var scrollPos = JSEvents.pageScrollPos();
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.detail, 'e.detail', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientWidth, 'document.body.clientWidth', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientHeight, 'document.body.clientHeight', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerWidth, 'window.innerWidth', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerHeight, 'window.innerHeight', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterWidth, 'window.outerWidth', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterHeight, 'window.outerHeight', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollTop, 'scrollPos[0]', 'i32') }}}
+ {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollLeft, 'scrollPos[1]', 'i32') }}}
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.uiEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: target,
+ allowsDeferredCalls: false, // Neither scroll or resize events allow running requests inside them.
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ getNodeNameForTarget: function(target) {
+ if (!target) return '';
+ if (target == window) return '#window';
+ if (target == window.screen) return '#screen';
+ return (target && target.nodeName) ? target.nodeName : '';
+ },
+
+ registerFocusEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.focusEvent) {
+ JSEvents.focusEvent = _malloc( {{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}} );
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ var nodeName = JSEvents.getNodeNameForTarget(e.target);
+ var id = e.target.id ? e.target.id : '';
+ writeStringToMemory(nodeName, JSEvents.focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.nodeName }}} );
+ writeStringToMemory(id, JSEvents.focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.id }}} );
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.focusEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ tick: function() {
+ if (window['performance'] && window['performance']['now']) return window['performance']['now']();
+ else return Date.now();
+ },
+
+ registerDeviceOrientationEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.deviceOrientationEvent) {
+ JSEvents.deviceOrientationEvent = _malloc( {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}} );
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.timestamp, 'JSEvents.tick()', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.alpha, 'e.alpha', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.beta, 'e.beta', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.gamma, 'e.gamma', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.absolute, 'e.absolute', 'i32') }}}
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.deviceOrientationEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ registerDeviceMotionEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.deviceMotionEvent) {
+ JSEvents.deviceMotionEvent = _malloc( {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}} );
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.timestamp, 'JSEvents.tick()', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationX, 'e.acceleration.x', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationY, 'e.acceleration.y', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationZ, 'e.acceleration.z', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityX, 'e.accelerationIncludingGravity.x', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityY, 'e.accelerationIncludingGravity.y', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityZ, 'e.accelerationIncludingGravity.z', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateAlpha, 'e.rotationRate.alpha', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateBeta, 'e.rotationRate.beta', 'double') }}}
+ {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateGamma, 'e.rotationRate.gamma', 'double') }}}
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.deviceMotionEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ screenOrientation: function() {
+ if (!window.screen) return undefined;
+ return window.screen.orientation || window.screen.mozOrientation || window.screen.webkitOrientation || window.screen.msOrientation;
+ },
+
+ fillOrientationChangeEventData: function(eventStruct, e) {
+ var orientations = ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"];
+ var orientations2 = ["portrait", "portrait", "landscape", "landscape"];
+
+ var orientationString = JSEvents.screenOrientation();
+ var orientation = orientations.indexOf(orientationString);
+ if (orientation == -1) {
+ orientation = orientations2.indexOf(orientationString);
+ }
+
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationIndex, '1 << orientation', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationAngle, 'window.orientation', 'i32') }}}
+ },
+
+ registerOrientationChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.orientationChangeEvent) {
+ JSEvents.orientationChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}} );
+ }
+
+ if (!target) {
+ target = window.screen; // Orientation events need to be captured from 'window.screen' instead of 'window'
+ } else {
+ target = JSEvents.findEventTarget(target);
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ JSEvents.fillOrientationChangeEventData(JSEvents.orientationChangeEvent, e);
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.orientationChangeEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ if (eventTypeString == "orientationchange" && window.screen.mozOrientation !== undefined) {
+ eventTypeString = "mozorientationchange";
+ }
+
+ var eventHandler = {
+ target: target,
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ fullscreenEnabled: function() {
+ return document.fullscreenEnabled || document.mozFullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled;
+ },
+
+ fillFullscreenChangeEventData: function(eventStruct, e) {
+ var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
+ var isFullscreen = !!fullscreenElement;
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.fullscreenEnabled, 'JSEvents.fullscreenEnabled()', 'i32') }}}
+ var nodeName = JSEvents.getNodeNameForTarget(fullscreenElement);
+ var id = (fullscreenElement && fullscreenElement.id) ? fullscreenElement.id : '';
+ writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.nodeName }}} );
+ writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.id }}} );
+ },
+
+ registerFullscreenChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.fullscreenChangeEvent) {
+ JSEvents.fullscreenChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}} );
+ }
+
+ if (!target) {
+ target = document; // Fullscreen change events need to be captured from 'document' by default instead of 'window'
+ } else {
+ target = JSEvents.findEventTarget(target);
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ JSEvents.fillFullscreenChangeEventData(JSEvents.fullscreenChangeEvent, e);
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.fullscreenChangeEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: target,
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ requestFullscreen: function(target) {
+ if (target.requestFullscreen) {
+ target.requestFullscreen();
+ } else if (target.msRequestFullscreen) {
+ target.msRequestFullscreen();
+ } else if (target.mozRequestFullScreen) {
+ target.mozRequestFullScreen();
+ } else if (target.mozRequestFullscreen) {
+ target.mozRequestFullscreen();
+ } else if (target.webkitRequestFullscreen) {
+ target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
+ } else {
+ if (typeof JSEvents.fullscreenEnabled() === 'undefined') {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}};
+ }
+ }
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ fillPointerlockChangeEventData: function(eventStruct, e) {
+ var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement;
+ var isPointerlocked = !!pointerLockElement;
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenPointerlockChangeEvent.isActive, 'isPointerlocked', 'i32') }}}
+ var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement);
+ var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : '';
+ writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.nodeName }}} );
+ writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.id }}});
+ },
+
+ registerPointerlockChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.pointerlockChangeEvent) {
+ JSEvents.pointerlockChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}} );
+ }
+
+ if (!target) {
+ target = document; // Pointer lock change events need to be captured from 'document' by default instead of 'window'
+ } else {
+ target = JSEvents.findEventTarget(target);
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ JSEvents.fillPointerlockChangeEventData(JSEvents.pointerlockChangeEvent, e);
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.pointerlockChangeEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: target,
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ requestPointerLock: function(target) {
+ if (target.requestPointerLock) {
+ target.requestPointerLock();
+ } else if (target.mozRequestPointerLock) {
+ target.mozRequestPointerLock();
+ } else if (target.webkitRequestPointerLock) {
+ target.webkitRequestPointerLock();
+ } else if (target.msRequestPointerLock) {
+ target.msRequestPointerLock();
+ } else {
+ // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element,
+ // or if the whole browser just doesn't support the feature.
+ if (document.body.requestPointerLock || document.body.mozRequestPointerLock || document.body.webkitRequestPointerLock || document.body.msRequestPointerLock) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}};
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ }
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ fillVisibilityChangeEventData: function(eventStruct, e) {
+ var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ];
+ var visibilityState = visibilityStates.indexOf(document.visibilityState);
+
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.hidden, 'document.hidden', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}}
+ },
+
+ registerVisibilityChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.visibilityChangeEvent) {
+ JSEvents.visibilityChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}} );
+ }
+
+ if (!target) {
+ target = document; // Visibility change events need to be captured from 'document' by default instead of 'window'
+ } else {
+ target = JSEvents.findEventTarget(target);
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ JSEvents.fillVisibilityChangeEventData(JSEvents.visibilityChangeEvent, e);
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.visibilityChangeEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: target,
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ registerTouchEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.touchEvent) {
+ JSEvents.touchEvent = _malloc( {{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}} );
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ var touches = {};
+ for(var i = 0; i < e.touches.length; ++i) {
+ var touch = e.touches[i];
+ touches[touch.identifier] = touch;
+ }
+ for(var i = 0; i < e.changedTouches.length; ++i) {
+ var touch = e.changedTouches[i];
+ touches[touch.identifier] = touch;
+ touch.changed = true;
+ }
+ for(var i = 0; i < e.targetTouches.length; ++i) {
+ var touch = e.targetTouches[i];
+ touches[touch.identifier].onTarget = true;
+ }
+
+ var ptr = JSEvents.touchEvent;
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.ctrlKey, 'e.ctrlKey', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.shiftKey, 'e.shiftKey', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.altKey, 'e.altKey', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.metaKey, 'e.metaKey', 'i32') }}}
+ ptr += {{{ C_STRUCTS.EmscriptenTouchEvent.touches }}}; // Advance to the start of the touch array.
+ var rect = Module['canvas'].getBoundingClientRect();
+ var numTouches = 0;
+ for(var i in touches) {
+ var t = touches[i];
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.identifier, 't.identifier', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.screenX, 't.screenX', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.screenY, 't.screenY', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.clientX, 't.clientX', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.clientY, 't.clientY', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.pageX, 't.pageX', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.pageY, 't.pageY', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.isChanged, 't.changed', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.onTarget, 't.onTarget', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.canvasX, 't.clientX - rect.left', 'i32') }}}
+ {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.canvasY, 't.clientY - rect.top', 'i32') }}}
+ ptr += {{{ C_STRUCTS.EmscriptenTouchPoint.__size__ }}};
+
+ if (++numTouches >= 32) {
+ break;
+ }
+ }
+ {{{ makeSetValue('JSEvents.touchEvent', C_STRUCTS.EmscriptenTouchEvent.numTouches, 'numTouches', 'i32') }}}
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.touchEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: true,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ fillGamepadEventData: function(eventStruct, e) {
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.timestamp, 'e.timestamp', 'double') }}}
+ for(var i = 0; i < e.axes.length; ++i) {
+ {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.axis, 'e.axes[i]', 'double') }}}
+ }
+ for(var i = 0; i < e.buttons.length; ++i) {
+ {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.analogButton, 'e.buttons[i].value', 'double') }}}
+ }
+ for(var i = 0; i < e.buttons.length; ++i) {
+ {{{ makeSetValue('eventStruct+i*4', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i32') }}}
+ }
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.connected, 'e.connected', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.index, 'e.index', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numAxes, 'e.axes.length', 'i32') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numButtons, 'e.buttons.length', 'i32') }}}
+ writeStringToMemory(e.id, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.id }}} );
+ writeStringToMemory(e.mapping, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.mapping }}} );
+ },
+
+ registerGamepadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.gamepadEvent) {
+ JSEvents.gamepadEvent = _malloc( {{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}} );
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ JSEvents.fillGamepadEventData(JSEvents.gamepadEvent, e.gamepad);
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.gamepadEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: true,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ registerBeforeUnloadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ var confirmationMessage = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, 0, userData]);
+
+ confirmationMessage = Pointer_stringify(confirmationMessage);
+ if (confirmationMessage) {
+ e.preventDefault();
+ e.returnValue = confirmationMessage;
+ return confirmationMessage;
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ battery: function() { return navigator.battery || navigator.mozBattery || navigator.webkitBattery; },
+
+ fillBatteryEventData: function(eventStruct, e) {
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.chargingTime, 'e.chargingTime', 'double') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.dischargingTime, 'e.dischargingTime', 'double') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.level, 'e.level', 'double') }}}
+ {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.charging, 'e.charging', 'i32') }}}
+ },
+
+ registerBatteryEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!JSEvents.batteryEvent) {
+ JSEvents.batteryEvent = _malloc( {{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}} );
+ }
+
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ JSEvents.fillBatteryEventData(JSEvents.batteryEvent, JSEvents.battery());
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.batteryEvent, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+
+ registerWebGlEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {
+ if (!target) {
+ target = Module['canvas'];
+ }
+ var handlerFunc = function(event) {
+ var e = event || window.event;
+
+ var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, 0, userData]);
+ if (shouldCancel) {
+ e.preventDefault();
+ }
+ };
+
+ var eventHandler = {
+ target: JSEvents.findEventTarget(target),
+ allowsDeferredCalls: false,
+ eventTypeString: eventTypeString,
+ callbackfunc: callbackfunc,
+ handlerFunc: handlerFunc,
+ useCapture: useCapture
+ };
+ JSEvents.registerOrRemoveHandler(eventHandler);
+ },
+ },
+
+ emscripten_set_keypress_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYPRESS') }}}, "keypress");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_keydown_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYDOWN') }}}, "keydown");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_keyup_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYUP') }}}, "keyup");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_click_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_CLICK') }}}, "click");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_mousedown_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEDOWN') }}}, "mousedown");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_mouseup_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEUP') }}}, "mouseup");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_dblclick_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DBLCLICK') }}}, "dblclick");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_mousemove_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEMOVE') }}}, "mousemove");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_mouse_status: function(mouseState) {
+ if (!JSEvents.mouseEvent) return {{{ cDefine('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.
+ HEAP32.set(HEAP32.subarray(JSEvents.mouseEvent, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}), mouseState);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_wheel_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_resize_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_RESIZE') }}}, "resize");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_scroll_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_SCROLL') }}}, "scroll");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_blur_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BLUR') }}}, "blur");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_focus_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUS') }}}, "focus");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_focusin_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSIN') }}}, "focusin");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_focusout_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSOUT') }}}, "focusout");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_deviceorientation_callback: function(userData, useCapture, callbackfunc) {
+ JSEvents.registerDeviceOrientationEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEORIENTATION') }}}, "deviceorientation");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_deviceorientation_status: function(orientationState) {
+ if (!JSEvents.deviceOrientationEvent) return {{{ cDefine('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.
+ HEAP32.set(HEAP32.subarray(JSEvents.deviceOrientationEvent, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}), orientationState);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_devicemotion_callback: function(userData, useCapture, callbackfunc) {
+ JSEvents.registerDeviceMotionEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEMOTION') }}}, "devicemotion");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_devicemotion_status: function(motionState) {
+ if (!JSEvents.deviceMotionEvent) return {{{ cDefine('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.
+ HEAP32.set(HEAP32.subarray(JSEvents.deviceMotionEvent, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}), motionState);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_orientationchange_callback: function(userData, useCapture, callbackfunc) {
+ if (!window.screen || !window.screen.addEventListener) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.registerOrientationChangeEventCallback(window.screen, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_ORIENTATIONCHANGE') }}}, "orientationchange");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_orientation_status: function(orientationChangeEvent) {
+ if (!JSEvents.screenOrientation() && typeof window.orientation === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.fillOrientationChangeEventData(orientationChangeEvent);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_lock_orientation: function(allowedOrientations) {
+ var orientations = [];
+ if (allowedOrientations & 1) orientations.push("portrait-primary");
+ if (allowedOrientations & 2) orientations.push("portrait-secondary");
+ if (allowedOrientations & 4) orientations.push("landscape-primary");
+ if (allowedOrientations & 8) orientations.push("landscape-secondary");
+ if (window.screen.lockOrientation) {
+ window.screen.lockOrientation(orientations);
+ } else if (window.screen.mozLockOrientation) {
+ window.screen.mozLockOrientation(orientations);
+ } else if (window.screen.webkitLockOrientation) {
+ window.screen.webkitLockOrientation(orientations);
+ } else if (window.screen.msLockOrientation) {
+ window.screen.msLockOrientation(orientations);
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_unlock_orientation: function() {
+ if (window.screen.unlockOrientation) {
+ window.screen.unlockOrientation();
+ } else if (window.screen.mozUnlockOrientation) {
+ window.screen.mozUnlockOrientation();
+ } else if (window.screen.webkitUnlockOrientation) {
+ window.screen.webkitUnlockOrientation();
+ } else if (window.screen.msUnlockOrientation) {
+ window.screen.msUnlockOrientation();
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_fullscreenchange_callback: function(target, userData, useCapture, callbackfunc) {
+ if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ if (!target) target = document;
+ else {
+ target = JSEvents.findEventTarget(target);
+ if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}};
+ }
+ JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "fullscreenchange");
+ JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "mozfullscreenchange");
+ JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "webkitfullscreenchange");
+ JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "msfullscreenchange");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_fullscreen_status: function(fullscreenStatus) {
+ if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.fillFullscreenChangeEventData(fullscreenStatus);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode
+ emscripten_request_fullscreen: function(target, deferUntilInEventHandler) {
+ if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ if (!JSEvents.fullscreenEnabled()) return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}};
+ if (!target) target = '#canvas';
+ target = JSEvents.findEventTarget(target);
+ if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}};
+
+ if (!target.requestFullscreen && !target.msRequestFullscreen && !target.mozRequestFullScreen && !target.mozRequestFullscreen && !target.webkitRequestFullscreen) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}};
+ }
+
+ var canPerformRequests = JSEvents.canPerformEventHandlerRequests();
+
+ // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so.
+ if (!canPerformRequests) {
+ if (deferUntilInEventHandler) {
+ JSEvents.deferCall(JSEvents.requestFullscreen, 1 /* priority over pointer lock */, [target]);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}};
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}};
+ }
+ }
+
+ return JSEvents.requestFullscreen(target);
+ },
+
+ emscripten_exit_fullscreen: function() {
+ if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ // Make sure no queued up calls will fire after this.
+ JSEvents.removeDeferredCalls(JSEvents.requestFullscreen);
+
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_pointerlockchange_callback: function(target, userData, useCapture, callbackfunc) {
+ if (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ if (!target) target = document;
+ else {
+ target = JSEvents.findEventTarget(target);
+ if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}};
+ }
+ JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "pointerlockchange");
+ JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "mozpointerlockchange");
+ JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "webkitpointerlockchange");
+ JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "mspointerlockchange");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_pointerlock_status: function(pointerlockStatus) {
+ if (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ JSEvents.fillPointerlockChangeEventData(pointerlockStatus);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_request_pointerlock: function(target, deferUntilInEventHandler) {
+ if (!target) target = '#canvas';
+ target = JSEvents.findEventTarget(target);
+ if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}};
+ if (!target.requestPointerLock && !target.mozRequestPointerLock && !target.webkitRequestPointerLock && !target.msRequestPointerLock) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+
+ var canPerformRequests = JSEvents.canPerformEventHandlerRequests();
+
+ // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so.
+ if (!canPerformRequests) {
+ if (deferUntilInEventHandler) {
+ JSEvents.deferCall(JSEvents.requestPointerLock, 2 /* priority below fullscreen */, [target]);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}};
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}};
+ }
+ }
+
+ return JSEvents.requestPointerLock(target);
+ },
+
+ emscripten_exit_pointerlock: function() {
+ // Make sure no queued up calls will fire after this.
+ JSEvents.removeDeferredCalls(JSEvents.requestPointerLock);
+
+ if (document.exitPointerLock) {
+ document.exitPointerLock();
+ } else if (document.msExitPointerLock) {
+ document.msExitPointerLock();
+ } else if (document.mozExitPointerLock) {
+ document.mozExitPointerLock();
+ } else if (document.webkitExitPointerLock) {
+ document.webkitExitPointerLock();
+ } else {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_vibrate: function(msecs) {
+ if (!navigator.vibrate) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ navigator.vibrate(msecs);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_vibrate_pattern: function(msecsArray, numEntries) {
+ if (!navigator.vibrate) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+
+ var vibrateList = [];
+ for(var i = 0; i < numEntries; ++i) {
+ var msecs = {{{ makeGetValue('msecsArray', 'i*4', 'i32') }}}
+ vibrateList.push(msecs);
+ }
+ navigator.vibrate(vibrateList);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_visibilitychange_callback: function(userData, useCapture, callbackfunc) {
+ JSEvents.registerVisibilityChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_VISIBILITYCHANGE') }}}, "visibilitychange");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_visibility_status: function(visibilityStatus) {
+ if (typeof document.visibilityState === 'undefined' && typeof document.hidden === 'undefined') {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ }
+ JSEvents.fillVisibilityChangeEventData(visibilityStatus);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_touchstart_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHSTART') }}}, "touchstart");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_touchend_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHEND') }}}, "touchend");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_touchmove_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHMOVE') }}}, "touchmove");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_touchcancel_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHCANCEL') }}}, "touchcancel");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_gamepadconnected_callback: function(userData, useCapture, callbackfunc) {
+ if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADCONNECTED') }}}, "gamepadconnected");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_gamepaddisconnected_callback: function(userData, useCapture, callbackfunc) {
+ if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED') }}}, "gamepaddisconnected");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_num_gamepads: function() {
+ if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ return navigator.getGamepads().length;
+ },
+
+ emscripten_get_gamepad_status: function(index, gamepadState) {
+ if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ var gamepads = navigator.getGamepads();
+ if (index < 0 || index >= gamepads.length) {
+ return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_PARAM') }}};
+ }
+ JSEvents.fillGamepadEventData(gamepadState, gamepads[index]);
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_beforeunload_callback: function(userData, callbackfunc) {
+ if (typeof window.onbeforeunload === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.registerBeforeUnloadEventCallback(window, userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BEFOREUNLOAD') }}}, "beforeunload");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_batterychargingchange_callback: function(userData, callbackfunc) {
+ if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.registerBatteryEventCallback(JSEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE') }}}, "chargingchange");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_batterylevelchange_callback: function(userData, callbackfunc) {
+ if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.registerBatteryEventCallback(JSEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE') }}}, "levelchange");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_get_battery_status: function(batteryState) {
+ if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}};
+ JSEvents.fillBatteryEventData(batteryState, JSEvents.battery());
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_webglcontextlost_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST') }}}, "webglcontextlost");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+
+ emscripten_set_webglcontextrestored_callback: function(target, userData, useCapture, callbackfunc) {
+ JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED') }}}, "webglcontextrestored");
+ return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
+ },
+};
+
+autoAddDeps(LibraryJSEvents, '$JSEvents');
+mergeInto(LibraryManager.library, LibraryJSEvents);
diff --git a/src/library_openal.js b/src/library_openal.js
index eb152f62..bb3af8fb 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -201,12 +201,15 @@ var LibraryOpenAL = {
}
if (ctx) {
+ var gain = ctx.createGain();
+ gain.connect(ctx.destination);
var context = {
ctx: ctx,
err: 0,
src: [],
buf: [],
- interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL)
+ interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL),
+ gain: gain
};
AL.contexts.push(context);
return AL.contexts.length;
@@ -254,7 +257,7 @@ var LibraryOpenAL = {
}
for (var i = 0; i < count; ++i) {
var gain = AL.currentContext.ctx.createGain();
- gain.connect(AL.currentContext.ctx.destination);
+ gain.connect(AL.currentContext.gain);
AL.currentContext.src.push({
state: 0x1011 /* AL_INITIAL */,
queue: [],
@@ -294,6 +297,34 @@ var LibraryOpenAL = {
this._velocity = val;
if (this.panner) this.panner.setVelocity(val[0], val[1], val[2]);
},
+ get direction() {
+ return this._direction || [0, 0, 0];
+ },
+ set direction(val) {
+ this._direction = val;
+ if (this.panner) this.panner.setOrientation(val[0], val[1], val[2]);
+ },
+ get coneOuterGain() {
+ return this._coneOuterGain || 0.0;
+ },
+ set coneOuterGain(val) {
+ this._coneOuterGain = val;
+ if (this.panner) this.panner.coneOuterGain = val;
+ },
+ get coneInnerAngle() {
+ return this._coneInnerAngle || 360.0;
+ },
+ set coneInnerAngle(val) {
+ this._coneInnerAngle = val;
+ if (this.panner) this.panner.coneInnerAngle = val;
+ },
+ get coneOuterAngle() {
+ return this._coneOuterAngle || 360.0;
+ },
+ set coneOuterAngle(val) {
+ this._coneOuterAngle = val;
+ if (this.panner) this.panner.coneOuterAngle = val;
+ },
gain: gain,
panner: null,
buffersPlayed: 0,
@@ -320,6 +351,12 @@ var LibraryOpenAL = {
return;
}
switch (param) {
+ case 0x1001 /* AL_CONE_INNER_ANGLE */:
+ src.coneInnerAngle = value;
+ break;
+ case 0x1002 /* AL_CONE_OUTER_ANGLE */:
+ src.coneOuterAngle = value;
+ break;
case 0x1007 /* AL_LOOPING */:
src.loop = (value === 1 /* AL_TRUE */);
break;
@@ -406,12 +443,15 @@ var LibraryOpenAL = {
case 0x1021 /* AL_ROLLOFF_FACTOR */:
src.rolloffFactor = value;
break;
- // case 0x1022 /* AL_CONE_OUTER_GAIN */:
- // break;
- // case 0x1001 /* AL_CONE_INNER_ANGLE */:
- // break;
- // case 0x1002 /* AL_CONE_OUTER_ANGLE */:
- // break;
+ case 0x1022 /* AL_CONE_OUTER_GAIN */:
+ src.coneOuterGain = value;
+ break;
+ case 0x1001 /* AL_CONE_INNER_ANGLE */:
+ src.coneInnerAngle = value;
+ break;
+ case 0x1002 /* AL_CONE_OUTER_ANGLE */:
+ src.coneOuterAngle = value;
+ break;
case 0x1020 /* AL_REFERENCE_DISTANCE */:
src.refDistance = value;
break;
@@ -443,6 +483,9 @@ var LibraryOpenAL = {
case 0x1004 /* AL_POSITION */:
src.position = [v1, v2, v3];
break;
+ case 0x1005 /* AL_DIRECTION */:
+ src.direction = [v1, v2, v3];
+ break;
case 0x1006 /* AL_VELOCITY */:
src.velocity = [v1, v2, v3];
break;
@@ -594,6 +637,21 @@ var LibraryOpenAL = {
}
},
+ alIsBuffer: function(bufferId) {
+ if (!AL.currentContext) {
+ return false;
+ }
+ if (bufferId > AL.currentContext.buf.length) {
+ return false;
+ }
+
+ if (!AL.currentContext.buf[bufferId - 1]) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+
alBufferData: function(buffer, format, data, size, freq) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
@@ -743,6 +801,12 @@ var LibraryOpenAL = {
case 0x202 /* AL_SOURCE_RELATIVE */:
{{{ makeSetValue('value', '0', 'src.panner ? 1 : 0', 'i32') }}};
break;
+ case 0x1001 /* AL_CONE_INNER_ANGLE */:
+ {{{ makeSetValue('value', '0', 'src.coneInnerAngle', 'i32') }}};
+ break;
+ case 0x1002 /* AL_CONE_OUTER_ANGLE */:
+ {{{ makeSetValue('value', '0', 'src.coneOuterAngle', 'i32') }}};
+ break;
case 0x1009 /* AL_BUFFER */:
if (!src.queue.length) {
{{{ makeSetValue('value', '0', '0', 'i32') }}};
@@ -809,12 +873,15 @@ var LibraryOpenAL = {
case 0x1021 /* AL_ROLLOFF_FACTOR */:
{{{ makeSetValue('value', '0', 'src.rolloffFactor', 'float') }}}
break;
- // case 0x1022 /* AL_CONE_OUTER_GAIN */:
- // break;
- // case 0x1001 /* AL_CONE_INNER_ANGLE */:
- // break;
- // case 0x1002 /* AL_CONE_OUTER_ANGLE */:
- // break;
+ case 0x1022 /* AL_CONE_OUTER_GAIN */:
+ {{{ makeSetValue('value', '0', 'src.coneOuterGain', 'float') }}}
+ break;
+ case 0x1001 /* AL_CONE_INNER_ANGLE */:
+ {{{ makeSetValue('value', '0', 'src.coneInnerAngle', 'float') }}}
+ break;
+ case 0x1002 /* AL_CONE_OUTER_ANGLE */:
+ {{{ makeSetValue('value', '0', 'src.coneOuterAngle', 'float') }}}
+ break;
case 0x1020 /* AL_REFERENCE_DISTANCE */:
{{{ makeSetValue('value', '0', 'src.refDistance', 'float') }}}
break;
@@ -830,6 +897,49 @@ var LibraryOpenAL = {
}
},
+ alGetSourcefv: function(source, param, values) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alGetSourcefv called without a valid context");
+#endif
+ return;
+ }
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
+#if OPENAL_DEBUG
+ console.error("alGetSourcefv called with an invalid source");
+#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
+ return;
+ }
+ switch (param) {
+ case 0x1004 /* AL_POSITION */:
+ var position = src.position;
+ {{{ makeSetValue('values', '0', 'position[0]', 'float') }}}
+ {{{ makeSetValue('values', '4', 'position[1]', 'float') }}}
+ {{{ makeSetValue('values', '8', 'position[2]', 'float') }}}
+ break;
+ case 0x1005 /* AL_DIRECTION */:
+ var direction = src.direction;
+ {{{ makeSetValue('values', '0', 'direction[0]', 'float') }}}
+ {{{ makeSetValue('values', '4', 'direction[1]', 'float') }}}
+ {{{ makeSetValue('values', '8', 'direction[2]', 'float') }}}
+ break;
+ case 0x1006 /* AL_VELOCITY */:
+ var velocity = src.velocity;
+ {{{ makeSetValue('values', '0', 'velocity[0]', 'float') }}}
+ {{{ makeSetValue('values', '4', 'velocity[1]', 'float') }}}
+ {{{ makeSetValue('values', '8', 'velocity[2]', 'float') }}}
+ break;
+ default:
+#if OPENAL_DEBUG
+ console.error("alGetSourcefv with param " + param + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
alDistanceModel: function(model) {
if (model !== 0 /* AL_NONE */) {
#if OPENAL_DEBUG
@@ -838,6 +948,136 @@ var LibraryOpenAL = {
}
},
+ alGetListenerf: function(pname, values) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alGetListenerf called without a valid context");
+#endif
+ return;
+ }
+ switch (pname) {
+ case 0x100A /* AL_GAIN */:
+ {{{ makeSetValue('value', '0', 'AL.currentContext.gain.gain', 'float') }}}
+ break;
+ default:
+#if OPENAL_DEBUG
+ console.error("alGetListenerf with param " + pname + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+
+ },
+
+ alGetListenerfv: function(pname, values) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alGetListenerfv called without a valid context");
+#endif
+ return;
+ }
+ switch (pname) {
+ case 0x1004 /* AL_POSITION */:
+ var position = AL.currentContext.ctx.listener._position || [0,0,0];
+ {{{ makeSetValue('values', '0', 'position[0]', 'float') }}}
+ {{{ makeSetValue('values', '4', 'position[1]', 'float') }}}
+ {{{ makeSetValue('values', '8', 'position[2]', 'float') }}}
+ break;
+ case 0x1006 /* AL_VELOCITY */:
+ var velocity = AL.currentContext.ctx.listener._velocity || [0,0,0];
+ {{{ makeSetValue('values', '0', 'velocity[0]', 'float') }}}
+ {{{ makeSetValue('values', '4', 'velocity[1]', 'float') }}}
+ {{{ makeSetValue('values', '8', 'velocity[2]', 'float') }}}
+ break;
+ case 0x100F /* AL_ORIENTATION */:
+ var orientation = AL.currentContext.ctx.listener._orientation || [0,0,0,0,0,0];
+ {{{ makeSetValue('values', '0', 'orientation[0]', 'float') }}}
+ {{{ makeSetValue('values', '4', 'orientation[1]', 'float') }}}
+ {{{ makeSetValue('values', '8', 'orientation[2]', 'float') }}}
+ {{{ makeSetValue('values', '12', 'orientation[3]', 'float') }}}
+ {{{ makeSetValue('values', '16', 'orientation[4]', 'float') }}}
+ {{{ makeSetValue('values', '20', 'orientation[5]', 'float') }}}
+ break;
+ default:
+#if OPENAL_DEBUG
+ console.error("alGetListenerfv with param " + pname + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
+ alGetListeneri: function(pname, value) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alGetListeneri called without a valid context");
+#endif
+ return;
+ }
+ switch (pname) {
+ default:
+#if OPENAL_DEBUG
+ console.error("alGetListeneri with param " + pname + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
+ alListenerf: function(param, value) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alListenerf called without a valid context");
+#endif
+ return;
+ }
+ switch (param) {
+ case 0x100A /* AL_GAIN */:
+ AL.currentContext.gain.value = value;
+ break;
+ default:
+#if OPENAL_DEBUG
+ console.error("alListenerf with param " + param + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
+ alEnable: function(param) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alEnable called without a valid context");
+#endif
+ return;
+ }
+ switch (param) {
+ default:
+#if OPENAL_DEBUG
+ console.error("alEnable with param " + param + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
+ alDisable: function(param) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alDisable called without a valid context");
+#endif
+ return;
+ }
+ switch (pname) {
+ default:
+#if OPENAL_DEBUG
+ console.error("alDisable with param " + param + " not implemented yet");
+#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
alListenerfv: function(param, values) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
@@ -847,32 +1087,32 @@ var LibraryOpenAL = {
}
switch (param) {
case 0x1004 /* AL_POSITION */:
- AL.currentContext.ctx.listener.setPosition(
- {{{ makeGetValue('values', '0', 'float') }}},
- {{{ makeGetValue('values', '4', 'float') }}},
- {{{ makeGetValue('values', '8', 'float') }}}
- );
+ var x = {{{ makeGetValue('value', '0', 'float') }}};
+ var y = {{{ makeGetValue('value', '4', 'float') }}};
+ var z = {{{ makeGetValue('value', '8', 'float') }}};
+ AL.currentContext.ctx.listener._position = [x,y,z];
+ AL.currentContext.ctx.listener.setPosition(x, y, z);
break;
case 0x1006 /* AL_VELOCITY */:
- AL.currentContext.ctx.listener.setVelocity(
- {{{ makeGetValue('values', '0', 'float') }}},
- {{{ makeGetValue('values', '4', 'float') }}},
- {{{ makeGetValue('values', '8', 'float') }}}
- );
+ var x = {{{ makeGetValue('value', '0', 'float') }}};
+ var y = {{{ makeGetValue('value', '4', 'float') }}};
+ var z = {{{ makeGetValue('value', '8', 'float') }}};
+ AL.currentContext.ctx.listener._velocity = [x,y,z];
+ AL.currentContext.ctx.listener.setVelocity(x, y, z);
break;
case 0x100F /* AL_ORIENTATION */:
- AL.currentContext.ctx.listener.setOrientation(
- {{{ makeGetValue('values', '0', 'float') }}},
- {{{ makeGetValue('values', '4', 'float') }}},
- {{{ makeGetValue('values', '8', 'float') }}},
- {{{ makeGetValue('values', '12', 'float') }}},
- {{{ makeGetValue('values', '16', 'float') }}},
- {{{ makeGetValue('values', '20', 'float') }}}
- );
+ var x = {{{ makeGetValue('value', '0', 'float') }}};
+ var y = {{{ makeGetValue('value', '4', 'float') }}};
+ var z = {{{ makeGetValue('value', '8', 'float') }}};
+ var x2 = {{{ makeGetValue('value', '12', 'float') }}};
+ var y2 = {{{ makeGetValue('value', '16', 'float') }}};
+ var z2 = {{{ makeGetValue('value', '20', 'float') }}};
+ AL.currentContext.ctx.listener._orientation = [x,y,z, x2, y2, z2];
+ AL.currentContext.ctx.listener.setOrientation(x,y,z,x2,y2,z2);
break;
default:
#if OPENAL_DEBUG
- console.log("alListenerfv with param " + param + " not implemented yet");
+ console.error("alListenerfv with param " + param + " not implemented yet");
#endif
AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
break;
diff --git a/src/modules.js b/src/modules.js
index d1ef4243..2d2a75d0 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -424,7 +424,7 @@ var LibraryManager = {
load: function() {
if (this.library) return;
- var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js'].concat(additionalLibraries);
+ var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_html5.js'].concat(additionalLibraries);
for (var i = 0; i < libraries.length; i++) {
var filename = libraries[i];
var src = read(filename);
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index 204986da..14c203e0 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -1,3 +1,7 @@
+// We are implementing the Relooper C API, so always export from this file.
+#ifndef RELOOPERDLL_EXPORTS
+#define RELOOPERDLL_EXPORTS
+#endif
#include "Relooper.h"
@@ -1269,7 +1273,7 @@ VoidIntMap __blockDebugMap__; // maps block pointers in currently running code t
extern "C" {
-void rl_set_output_buffer(char *buffer, int size) {
+RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size) {
#if DEBUG
printf("#include \"Relooper.h\"\n");
printf("int main() {\n");
@@ -1279,15 +1283,15 @@ void rl_set_output_buffer(char *buffer, int size) {
Relooper::SetOutputBuffer(buffer, size);
}
-void rl_make_output_buffer(int size) {
+RELOOPERDLL_API void rl_make_output_buffer(int size) {
Relooper::SetOutputBuffer((char*)malloc(size), size);
}
-void rl_set_asm_js_mode(int on) {
+RELOOPERDLL_API void rl_set_asm_js_mode(int on) {
Relooper::SetAsmJSMode(on);
}
-void *rl_new_block(const char *text, const char *branch_var) {
+RELOOPERDLL_API void *rl_new_block(const char *text, const char *branch_var) {
Block *ret = new Block(text, branch_var);
#if DEBUG
printf(" void *b%d = rl_new_block(\"// code %d\");\n", ret->Id, ret->Id);
@@ -1297,21 +1301,21 @@ void *rl_new_block(const char *text, const char *branch_var) {
return ret;
}
-void rl_delete_block(void *block) {
+RELOOPERDLL_API void rl_delete_block(void *block) {
#if DEBUG
printf(" rl_delete_block(block_map[%d]);\n", ((Block*)block)->Id);
#endif
delete (Block*)block;
}
-void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code) {
+RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code) {
#if DEBUG
printf(" rl_block_add_branch_to(block_map[%d], block_map[%d], %s%s%s, %s%s%s);\n", ((Block*)from)->Id, ((Block*)to)->Id, condition ? "\"" : "", condition ? condition : "NULL", condition ? "\"" : "", code ? "\"" : "", code ? code : "NULL", code ? "\"" : "");
#endif
((Block*)from)->AddBranchTo((Block*)to, condition, code);
}
-void *rl_new_relooper() {
+RELOOPERDLL_API void *rl_new_relooper() {
#if DEBUG
printf(" void *block_map[10000];\n");
printf(" void *rl = rl_new_relooper();\n");
@@ -1319,18 +1323,18 @@ void *rl_new_relooper() {
return new Relooper;
}
-void rl_delete_relooper(void *relooper) {
+RELOOPERDLL_API void rl_delete_relooper(void *relooper) {
delete (Relooper*)relooper;
}
-void rl_relooper_add_block(void *relooper, void *block) {
+RELOOPERDLL_API void rl_relooper_add_block(void *relooper, void *block) {
#if DEBUG
printf(" rl_relooper_add_block(rl, block_map[%d]);\n", ((Block*)block)->Id);
#endif
((Relooper*)relooper)->AddBlock((Block*)block);
}
-void rl_relooper_calculate(void *relooper, void *entry) {
+RELOOPERDLL_API void rl_relooper_calculate(void *relooper, void *entry) {
#if DEBUG
printf(" rl_relooper_calculate(rl, block_map[%d]);\n", ((Block*)entry)->Id);
printf(" rl_relooper_render(rl);\n");
@@ -1342,7 +1346,7 @@ void rl_relooper_calculate(void *relooper, void *entry) {
((Relooper*)relooper)->Calculate((Block*)entry);
}
-void rl_relooper_render(void *relooper) {
+RELOOPERDLL_API void rl_relooper_render(void *relooper) {
((Relooper*)relooper)->Render();
}
diff --git a/src/shell.html b/src/shell.html
index efb9e91d..7a3a8d08 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -11,10 +11,44 @@
div.emscripten_border { border: 1px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten { border: 0px none; }
+
+ .spinner {
+ height: 50px;
+ width: 50px;
+ margin: 0px auto;
+ -webkit-animation: rotation .8s linear infinite;
+ -moz-animation: rotation .8s linear infinite;
+ -o-animation: rotation .8s linear infinite;
+ animation: rotation 0.8s linear infinite;
+ border-left: 10px solid rgb(0,150,240);
+ border-right: 10px solid rgb(0,150,240);
+ border-bottom: 10px solid rgb(0,150,240);
+ border-top: 10px solid rgb(100,0,200);
+ border-radius: 100%;
+ background-color: rgb(200,100,250);
+ }
+ @-webkit-keyframes rotation {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(360deg);}
+ }
+ @-moz-keyframes rotation {
+ from {-moz-transform: rotate(0deg);}
+ to {-moz-transform: rotate(360deg);}
+ }
+ @-o-keyframes rotation {
+ from {-o-transform: rotate(0deg);}
+ to {-o-transform: rotate(360deg);}
+ }
+ @keyframes rotation {
+ from {transform: rotate(0deg);}
+ to {transform: rotate(360deg);}
+ }
+
</style>
</head>
<body>
<hr/>
+ <figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
@@ -35,7 +69,10 @@
<textarea class="emscripten" id="output" rows="8"></textarea>
<hr>
<script type='text/javascript'>
- // connect to canvas
+ var statusElement = document.getElementById('status');
+ var progressElement = document.getElementById('progress');
+ var spinnerElement = document.getElementById('spinner');
+
var Module = {
preRun: [],
postRun: [],
@@ -68,17 +105,17 @@
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon
- var statusElement = document.getElementById('status');
- var progressElement = document.getElementById('progress');
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
+ spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
+ if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
},
diff --git a/src/struct_info.json b/src/struct_info.json
index a22851b6..32261c0a 100644
--- a/src/struct_info.json
+++ b/src/struct_info.json
@@ -1073,5 +1073,188 @@
"UUID_TYPE_DCE_RANDOM"
],
"structs": {}
+ },
+ // ===========================================
+ // emscripten html5 library
+ // ===========================================
+ {
+ "file": "emscripten/html5.h",
+ "defines": [
+ "EMSCRIPTEN_EVENT_KEYPRESS",
+ "EMSCRIPTEN_EVENT_KEYDOWN",
+ "EMSCRIPTEN_EVENT_KEYUP",
+ "EMSCRIPTEN_EVENT_CLICK",
+ "EMSCRIPTEN_EVENT_MOUSEDOWN",
+ "EMSCRIPTEN_EVENT_MOUSEUP",
+ "EMSCRIPTEN_EVENT_DBLCLICK",
+ "EMSCRIPTEN_EVENT_MOUSEMOVE",
+ "EMSCRIPTEN_EVENT_WHEEL",
+ "EMSCRIPTEN_EVENT_RESIZE",
+ "EMSCRIPTEN_EVENT_SCROLL",
+ "EMSCRIPTEN_EVENT_BLUR",
+ "EMSCRIPTEN_EVENT_FOCUS",
+ "EMSCRIPTEN_EVENT_FOCUSIN",
+ "EMSCRIPTEN_EVENT_FOCUSOUT",
+ "EMSCRIPTEN_EVENT_DEVICEORIENTATION",
+ "EMSCRIPTEN_EVENT_DEVICEMOTION",
+ "EMSCRIPTEN_EVENT_ORIENTATIONCHANGE",
+ "EMSCRIPTEN_EVENT_FULLSCREENCHANGE",
+ "EMSCRIPTEN_EVENT_POINTERLOCKCHANGE",
+ "EMSCRIPTEN_EVENT_VISIBILITYCHANGE",
+ "EMSCRIPTEN_EVENT_TOUCHSTART",
+ "EMSCRIPTEN_EVENT_TOUCHEND",
+ "EMSCRIPTEN_EVENT_TOUCHMOVE",
+ "EMSCRIPTEN_EVENT_TOUCHCANCEL",
+ "EMSCRIPTEN_EVENT_GAMEPADCONNECTED",
+ "EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED",
+ "EMSCRIPTEN_EVENT_BEFOREUNLOAD",
+ "EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE",
+ "EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE",
+ "EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST",
+ "EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED",
+
+ "EMSCRIPTEN_RESULT_SUCCESS",
+ "EMSCRIPTEN_RESULT_DEFERRED",
+ "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED",
+ "EMSCRIPTEN_RESULT_INVALID_TARGET",
+ "EMSCRIPTEN_RESULT_UNKNOWN_TARGET",
+ "EMSCRIPTEN_RESULT_INVALID_PARAM",
+ "EMSCRIPTEN_RESULT_NOT_SUPPORTED",
+ "EMSCRIPTEN_RESULT_FAILED",
+ "EMSCRIPTEN_RESULT_NO_DATA"
+ ],
+ "structs": {
+ "EmscriptenKeyboardEvent": [
+ "key",
+ "code",
+ "location",
+ "ctrlKey",
+ "shiftKey",
+ "altKey",
+ "metaKey",
+ "repeat",
+ "locale",
+ "charValue",
+ "charCode",
+ "keyCode",
+ "which"
+ ],
+ "EmscriptenMouseEvent": [
+ "timestamp",
+ "screenX",
+ "screenY",
+ "clientX",
+ "clientY",
+ "ctrlKey",
+ "shiftKey",
+ "altKey",
+ "metaKey",
+ "button",
+ "buttons",
+ "movementX",
+ "movementY",
+ "canvasX",
+ "canvasY"
+ ],
+ "EmscriptenWheelEvent": [
+ "mouse",
+ "deltaX",
+ "deltaY",
+ "deltaZ",
+ "deltaMode"
+ ],
+ "EmscriptenUiEvent": [
+ "detail",
+ "documentBodyClientWidth",
+ "documentBodyClientHeight",
+ "windowInnerWidth",
+ "windowInnerHeight",
+ "windowOuterWidth",
+ "windowOuterHeight",
+ "scrollTop",
+ "scrollLeft"
+ ],
+ "EmscriptenFocusEvent": [
+ "nodeName",
+ "id"
+ ],
+ "EmscriptenDeviceOrientationEvent": [
+ "timestamp",
+ "alpha",
+ "beta",
+ "gamma",
+ "absolute"
+ ],
+ "EmscriptenDeviceMotionEvent": [
+ "timestamp",
+ "accelerationX",
+ "accelerationY",
+ "accelerationZ",
+ "accelerationIncludingGravityX",
+ "accelerationIncludingGravityY",
+ "accelerationIncludingGravityZ",
+ "rotationRateAlpha",
+ "rotationRateBeta",
+ "rotationRateGamma"
+ ],
+ "EmscriptenOrientationChangeEvent": [
+ "orientationIndex",
+ "orientationAngle"
+ ],
+ "EmscriptenFullscreenChangeEvent": [
+ "isFullscreen",
+ "fullscreenEnabled",
+ "nodeName",
+ "id"
+ ],
+ "EmscriptenPointerlockChangeEvent": [
+ "isActive",
+ "nodeName",
+ "id"
+ ],
+ "EmscriptenVisibilityChangeEvent": [
+ "hidden",
+ "visibilityState"
+ ],
+ "EmscriptenTouchPoint": [
+ "identifier",
+ "screenX",
+ "screenY",
+ "clientX",
+ "clientY",
+ "pageX",
+ "pageY",
+ "isChanged",
+ "onTarget",
+ "canvasX",
+ "canvasY"
+ ],
+ "EmscriptenTouchEvent": [
+ "numTouches",
+ "ctrlKey",
+ "shiftKey",
+ "altKey",
+ "metaKey",
+ "touches"
+ ],
+ "EmscriptenGamepadEvent": [
+ "timestamp",
+ "axis",
+ "analogButton",
+ "digitalButton",
+ "connected",
+ "index",
+ "numAxes",
+ "numButtons",
+ "id",
+ "mapping"
+ ],
+ "EmscriptenBatteryEvent": [
+ "chargingTime",
+ "dischargingTime",
+ "level",
+ "charging"
+ ]
+ }
}
-]
+ ]
diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h
index b6e6307b..fb456d67 100644
--- a/system/include/emscripten/emscripten.h
+++ b/system/include/emscripten/emscripten.h
@@ -227,7 +227,14 @@ void emscripten_get_canvas_size(int *width, int *height, int *isFullscreen);
* absolute time, and is only meaningful in comparison to
* other calls to this function. The unit is ms.
*/
+#if EMSCRIPTEN
double emscripten_get_now();
+#else
+#include <time.h>
+double emscripten_get_now() {
+ return (1000*clock())/(double)CLOCKS_PER_SEC;
+}
+#endif
/*
* Simple random number generation in [0, 1), maps to Math.random().
diff --git a/system/include/emscripten/html5.h b/system/include/emscripten/html5.h
new file mode 100644
index 00000000..06c647bf
--- /dev/null
+++ b/system/include/emscripten/html5.h
@@ -0,0 +1,628 @@
+#ifndef __emscripten_events_h__
+#define __emscripten_events_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This file defines Emscripten low-level glue bindings for interfacing with the following HTML5 APIs:
+ * - DOM Level 3 Events: Keyboard, Mouse, Mouse Wheel, Resize, Scroll, Focus. See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html
+ * - DeviceOrientation Events for gyro and accelerometer. See http://www.w3.org/TR/orientation-event/
+ * - Screen Orientation Events for portrait/landscape handling. See https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html
+ * - Fullscreen Events for browser canvas fullscreen modes transitioning. See https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
+ * - Pointer Lock Events for relative-mode mouse motion control. See http://www.w3.org/TR/pointerlock/
+ * - Vibration API for mobile device haptic vibration feedback control. See http://dev.w3.org/2009/dap/vibration/
+ * - Page Visibility Events for power management control. See http://www.w3c-test.org/webperf/specs/PageVisibility/
+ * - Touch Events. See http://www.w3.org/TR/touch-events/
+ * - Gamepad API. See http://www.w3.org/TR/gamepad/
+ * - Beforeunload event. See http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#beforeunloadevent
+ * - WebGL context events. See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ *
+ * Most web APIs are event-based, which means that the functionality is accessed by registering a callback function to be called when the event occurs. The
+ * Gamepad API is currently an exception, for which only a polling API is available. For some APIs, both an event-based and a polling-based API is exposed.
+ *
+ * Calling a callback registration function with a null pointer function causes an unregistration of that callback from the given target element. All event
+ * handlers are also automatically unregistered when the C exit() function is invoked during the atexit handler pass. Use either the function
+ * emscripten_set_main_loop() or set Module.noExitRuntime = true; to make sure that leaving main() will not immediately cause an exit() and clean up the
+ * event handlers.
+ *
+ * Throughout this file, the function signatures have a 'target' parameter. This parameter allows specifying the HTML Element ID to which the callback
+ * registration is to be applied to. This field has the following special meanings:
+ * - 0 or NULL: A default element is chosen automatically based on the event type, which should be reasonable most of the time.
+ * - "#window": The event listener is applied to the JS 'window' object.
+ * - "#document": The event listener is applied to the JS 'document' object.
+ * - "#screen": The event listener is applied to the JS 'window.screen' object.
+ * - "#canvas": The event listener is applied to the Emscripten default WebGL canvas element.
+ * - Any other string without a leading hash "#" sign: The event listener is applied to the element by the given ID on the page.
+ *
+ * The callback hook functions also take in a 'userData' parameter. This is a custom user-defineable value that will be carried through unchanged to all
+ * invocations of the registered event callback. Use this e.g. to pass a pointer to a C++ class or similar to enclose the C API in a clean object-oriented manner.
+ *
+ * Callback handlers that return an EM_BOOL may return nonzero to signal that the default action for that event is to be suppressed. This will call
+ * the .preventDefault(); member on the event. Returning zero will cause the default browser event action to be carried out.
+ *
+ * Most functions return the result using the type EMSCRIPTEN_RESULT. Nonzero and positive values denote success. Negative values
+ * signal failure. None of the functions fail or abort by throwing a JS or C++ exception. If a particular browser does not support the given feature,
+ * the value EMSCRIPTEN_RESULT_NOT_SUPPORTED will be returned at the time the callback is registered.
+ *
+ * Due to web security purposes, the pointer lock and fullscreen requests can only be invoked from inside an user-originated event handler. Such requests
+ * are automatically deferred until the user presses a keyboard or mouse button the next time.
+ */
+
+#define EMSCRIPTEN_EVENT_KEYPRESS 1
+#define EMSCRIPTEN_EVENT_KEYDOWN 2
+#define EMSCRIPTEN_EVENT_KEYUP 3
+#define EMSCRIPTEN_EVENT_CLICK 4
+#define EMSCRIPTEN_EVENT_MOUSEDOWN 5
+#define EMSCRIPTEN_EVENT_MOUSEUP 6
+#define EMSCRIPTEN_EVENT_DBLCLICK 7
+#define EMSCRIPTEN_EVENT_MOUSEMOVE 8
+#define EMSCRIPTEN_EVENT_WHEEL 9
+#define EMSCRIPTEN_EVENT_RESIZE 10
+#define EMSCRIPTEN_EVENT_SCROLL 11
+#define EMSCRIPTEN_EVENT_BLUR 12
+#define EMSCRIPTEN_EVENT_FOCUS 13
+#define EMSCRIPTEN_EVENT_FOCUSIN 14
+#define EMSCRIPTEN_EVENT_FOCUSOUT 15
+#define EMSCRIPTEN_EVENT_DEVICEORIENTATION 16
+#define EMSCRIPTEN_EVENT_DEVICEMOTION 17
+#define EMSCRIPTEN_EVENT_ORIENTATIONCHANGE 18
+#define EMSCRIPTEN_EVENT_FULLSCREENCHANGE 19
+#define EMSCRIPTEN_EVENT_POINTERLOCKCHANGE 20
+#define EMSCRIPTEN_EVENT_VISIBILITYCHANGE 21
+#define EMSCRIPTEN_EVENT_TOUCHSTART 22
+#define EMSCRIPTEN_EVENT_TOUCHEND 23
+#define EMSCRIPTEN_EVENT_TOUCHMOVE 24
+#define EMSCRIPTEN_EVENT_TOUCHCANCEL 25
+#define EMSCRIPTEN_EVENT_GAMEPADCONNECTED 26
+#define EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED 27
+#define EMSCRIPTEN_EVENT_BEFOREUNLOAD 28
+#define EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE 29
+#define EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE 30
+#define EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST 31
+#define EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED 32
+
+#define EMSCRIPTEN_RESULT int
+
+// The operation succeeded
+#define EMSCRIPTEN_RESULT_SUCCESS 0
+
+// For web security reasons, the requested operation cannot be completed now, but was deferred for completion in the next event handler.
+#define EMSCRIPTEN_RESULT_DEFERRED 1
+
+// The given operation is not supported by this browser or the target element.
+#define EMSCRIPTEN_RESULT_NOT_SUPPORTED -1
+
+// For web security reasons, the requested operation could not be completed now, and it failed since the user requested the operation to not be deferred.
+#define EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED -2
+
+// The given target element for the operation is invalid.
+#define EMSCRIPTEN_RESULT_INVALID_TARGET -3
+
+// The given target element for the operation was not found.
+#define EMSCRIPTEN_RESULT_UNKNOWN_TARGET -4
+
+// An invalid parameter was passed to the function.
+#define EMSCRIPTEN_RESULT_INVALID_PARAM -5
+
+// The operation failed due to some generic reason.
+#define EMSCRIPTEN_RESULT_FAILED -6
+
+// Operation failed since no data is currently available.
+#define EMSCRIPTEN_RESULT_NO_DATA -7
+
+#define EM_BOOL int
+#define EM_UTF8 char
+
+#define DOM_KEY_LOCATION int
+#define DOM_KEY_LOCATION_STANDARD 0x00
+#define DOM_KEY_LOCATION_LEFT 0x01
+#define DOM_KEY_LOCATION_RIGHT 0x02
+#define DOM_KEY_LOCATION_NUMPAD 0x03
+
+/*
+ * The event structure passed in keyboard events keypress, keydown and keyup.
+ * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys
+ */
+typedef struct EmscriptenKeyboardEvent {
+ // The printed representation of the pressed key.
+ EM_UTF8 key[32];
+ // A string that identifies the physical key being pressed. The value is not affected by the current keyboard
+ // layout or modifier state, so a particular key will always return the same value.
+ EM_UTF8 code[32];
+ // Indicates the location of the key on the keyboard. One of the DOM_KEY_LOCATION_ values.
+ unsigned long location;
+ // Specifies which modifiers were active during the key event.
+ EM_BOOL ctrlKey;
+ EM_BOOL shiftKey;
+ EM_BOOL altKey;
+ EM_BOOL metaKey;
+ // Specifies if this keyboard event represents a repeated press.
+ EM_BOOL repeat;
+ // A locale string indicating the locale the keyboard is configured for. This may be the empty string if the
+ // browser or device doesn't know the keyboard's locale.
+ EM_UTF8 locale[32];
+ // The following fields are values from previous versions of the DOM key events specifications.
+ // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent?redirectlocale=en-US&redirectslug=DOM%2FKeyboardEvent
+ // The character representation of the key.
+ EM_UTF8 charValue[32];
+ // The Unicode reference number of the key; this attribute is used only by the keypress event. For keys whose char attribute
+ // contains multiple characters, this is the Unicode value of the first character in that attribute.
+ unsigned long charCode;
+ // A system and implementation dependent numerical code identifying the unmodified value of the pressed key.
+ unsigned long keyCode;
+ // A system and implementation dependent numeric code identifying the unmodified value of the pressed key; this is usually the same as keyCode.
+ unsigned long which;
+} EmscriptenKeyboardEvent;
+
+/*
+ * Registers a callback function for receiving browser-generated keyboard input events.
+ * See https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent
+ * and http://www.javascriptkit.com/jsref/eventkeyboardmouse.shtml
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_keypress_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_keydown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_keyup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData));
+
+/*
+ * The event structure passed in mouse events click, mousedown, mouseup, dblclick and mousemove.
+ * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-MouseEvent
+ */
+typedef struct EmscriptenMouseEvent {
+ // A timestamp of when this data was generated by the browser. This is an absolute wallclock time in milliseconds.
+ double timestamp;
+ // The coordinate relative to the browser screen coordinate system.
+ long screenX;
+ long screenY;
+ // The coordinate relative to the viewport associate with the event.
+ long clientX;
+ long clientY;
+ // Specifies which modifiers were active during the mouse event.
+ EM_BOOL ctrlKey;
+ EM_BOOL shiftKey;
+ EM_BOOL altKey;
+ EM_BOOL metaKey;
+ // Which pointer device button changed state.
+ unsigned short button;
+ // A bitmask that indicates which combinations of mouse buttons were being held down at the time of the event.
+ unsigned short buttons;
+ // If pointer lock is active, these two extra fields give relative mouse movement since the last event.
+ long movementX;
+ long movementY;
+ // Emscripten-specific extension: These fields give the mouse coordinates mapped to the Emscripten canvas client area.
+ long canvasX;
+ long canvasY;
+ // Pad this struct to multiple of 8 bytes to make WheelEvent unambiguously align to 8 bytes.
+ long padding;
+} EmscriptenMouseEvent;
+
+/*
+ * Registers a callback function for receiving browser-generated mouse input events.
+ * See https://developer.mozilla.org/en/DOM/MouseEvent
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_dblclick_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_mousemove_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData));
+/*
+ * Returns the most recently received mouse event state. Note that for this function call to succeed, emscripten_set_xx_callback must have first
+ * been called with one of the mouse event types and a non-zero callback function pointer to enable the Mouse state capture.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_mouse_status(EmscriptenMouseEvent *mouseState);
+
+#define DOM_DELTA_PIXEL 0x00
+#define DOM_DELTA_LINE 0x01
+#define DOM_DELTA_PAGE 0x02
+
+/*
+ * The event structure passed in mouse wheelevent.
+ * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-WheelEvent
+ */
+typedef struct EmscriptenWheelEvent {
+ // Specifies general mouse information related to this event.
+ EmscriptenMouseEvent mouse;
+ // Measures along different axes the movement of the wheel.
+ double deltaX;
+ double deltaY;
+ double deltaZ;
+ // One of the DOM_DELTA_ values that indicates the units of measurement for the delta values.
+ unsigned long deltaMode;
+} EmscriptenWheelEvent;
+
+/*
+ * Registers a callback function for receiving browser-generated mouse wheel events.
+ * See http://www.w3.org/TR/DOM-Level-3-Events/#event-type-wheel
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData));
+
+/*
+ * The event structure passed in DOM element resize and scroll events.
+ * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-UIEvent
+ */
+typedef struct EmscriptenUiEvent {
+ // Specifies detail information about this event.
+ long detail;
+ // The clientWidth/clientHeight of the document.body element.
+ int documentBodyClientWidth;
+ int documentBodyClientHeight;
+ // The innerWidth/innerHeight of the window element.
+ int windowInnerWidth;
+ int windowInnerHeight;
+ // The outerWidth/outerHeight of the window element.
+ int windowOuterWidth;
+ int windowOuterHeight;
+ // The page scroll position.
+ int scrollTop;
+ int scrollLeft;
+} EmscriptenUiEvent;
+
+/*
+ * Registers a callback function for receiving DOM element resize and scroll events.
+ * See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData));
+
+/*
+ * The event structure passed in DOM element blur, focus, focusin and focusout events.
+ * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-FocusEvent
+ */
+typedef struct EmscriptenFocusEvent {
+ // The nodeName of the target HTML Element. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName
+ EM_UTF8 nodeName[128];
+ // The HTML Element ID of the target element.
+ EM_UTF8 id[128];
+} EmscriptenFocusEvent;
+
+/*
+ * Registers a callback function for receiving DOM element blur, focus, focusin and focusout events.
+ * See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-blur
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_focusout_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData));
+
+/*
+ * The event structure passed in the deviceorientation event.
+ * http://dev.w3.org/geo/api/spec-source-orientation.html#deviceorientation
+ */
+typedef struct EmscriptenDeviceOrientationEvent {
+ // Absolute wallclock time in msec units of when the event occurred.
+ double timestamp;
+ // The orientation of the device in terms of the transformation from a coordinate frame fixed on the Earth to a coordinate frame fixed in the device.
+ double alpha;
+ double beta;
+ double gamma;
+ // If false, the orientation is only relative to some other bas orinetation, not to the fixed coordinate frame.
+ EM_BOOL absolute;
+} EmscriptenDeviceOrientationEvent;
+
+/*
+ * Registers a callback function for receiving the deviceorientation event.
+ * See http://dev.w3.org/geo/api/spec-source-orientation.html
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenDeviceOrientationEvent *orientationEvent, void *userData));
+/*
+ * Returns the most recently received deviceorientation event state. Note that for this function call to succeed, emscripten_set_deviceorientation_callback
+ * must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the Device Orientation state capture.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_deviceorientation_status(EmscriptenDeviceOrientationEvent *orientationState);
+
+/*
+ * The event structure passed in the devicemotion event.
+ * http://dev.w3.org/geo/api/spec-source-orientation.html#devicemotion
+ */
+typedef struct EmscriptenDeviceMotionEvent {
+ // Absolute wallclock time in msec units of when the event occurred.
+ double timestamp;
+ // Acceleration of the device excluding gravity.
+ double accelerationX;
+ double accelerationY;
+ double accelerationZ;
+ // Acceleration of the device including gravity.
+ double accelerationIncludingGravityX;
+ double accelerationIncludingGravityY;
+ double accelerationIncludingGravityZ;
+ // The rotational delta of the device.
+ double rotationRateAlpha;
+ double rotationRateBeta;
+ double rotationRateGamma;
+} EmscriptenDeviceMotionEvent;
+
+/*
+ * Registers a callback function for receiving the devicemotion event.
+ * See http://dev.w3.org/geo/api/spec-source-orientation.html
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenDeviceMotionEvent *motionEvent, void *userData));
+/*
+ * Returns the most recently received deviceomotion event state. Note that for this function call to succeed, emscripten_set_devicemotion_callback
+ * must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the Device Motion state capture.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_devicemotion_status(EmscriptenDeviceMotionEvent *motionState);
+
+#define EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY 1
+#define EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY 2
+#define EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY 4
+#define EMSCRIPTEN_ORIENTATION_LANDSCAPE_SECONDARY 8
+
+/*
+ * The event structure passed in the orientationchange event.
+ * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html
+ */
+typedef struct EmscriptenOrientationChangeEvent {
+ // One of EM_ORIENTATION_PORTRAIT_xx fields, or -1 if unknown.
+ int orientationIndex;
+ // Emscripten-specific extension: Some browsers refer to 'window.orientation', so report that as well.
+ // Orientation angle in degrees. 0: "default orientation", i.e. default upright orientation to hold the mobile device in. Could be either landscape or portrait.
+ int orientationAngle;
+} EmscriptenOrientationChangeEvent;
+
+/*
+ * Registers a callback function for receiving the orientationchange event.
+ * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenOrientationChangeEvent *orientationChangeEvent, void *userData));
+/*
+ * Returns the current device orientation state.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_orientation_status(EmscriptenOrientationChangeEvent *orientationStatus);
+/*
+ * Locks the screen orientation to the given set of allowed orientations.
+ * allowedOrientations: A bitfield set of EM_ORIENTATION_xx flags.
+ */
+extern EMSCRIPTEN_RESULT emscripten_lock_orientation(int allowedOrientations);
+/*
+ * Allows the screen to turn again into any orientation.
+ */
+extern EMSCRIPTEN_RESULT emscripten_unlock_orientation();
+
+/*
+ * The event structure passed in the fullscreenchange event.
+ * https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
+ */
+typedef struct EmscriptenFullscreenChangeEvent {
+ // Specifies whether an element on the browser page is currently fullscreen.
+ EM_BOOL isFullscreen;
+ // Specifies if the current page has the ability to display elements fullscreen.
+ EM_BOOL fullscreenEnabled;
+ // The nodeName of the target HTML Element that is in full screen mode. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName
+ EM_UTF8 nodeName[128];
+ // The HTML Element ID of the target HTML element that is in full screen mode.
+ EM_UTF8 id[128];
+} EmscriptenFullscreenChangeEvent;
+
+/*
+ * Registers a callback function for receiving the fullscreenchange event.
+ * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData));
+/*
+ * Returns the current page fullscreen state.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_fullscreen_status(EmscriptenFullscreenChangeEvent *fullscreenStatus);
+/*
+ * Requests the given target element to transition to full screen mode.
+ * Note: This function can only run inside a user-generated JavaScript event handler.
+ * deferUntilInEventHandler: If true and you called this function outside an event callback, this request will
+ * be queued to be executed the next time a JS event handler runs. If false, this
+ * function will instead fail if not running inside a JS event handler.
+ */
+extern EMSCRIPTEN_RESULT emscripten_request_fullscreen(const char *target, int deferUntilInEventHandler);
+/*
+ * Returns back to windowed browsing mode.
+ */
+extern EMSCRIPTEN_RESULT emscripten_exit_fullscreen();
+
+/*
+ * The event structure passed in the pointerlockchange event.
+ * http://www.w3.org/TR/pointerlock/
+ */
+typedef struct EmscriptenPointerlockChangeEvent {
+ // Specifies whether an element on the browser page currently has pointer lock enabled.
+ EM_BOOL isActive;
+ // The nodeName of the target HTML Element that has the pointer lock active. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName
+ EM_UTF8 nodeName[128];
+ // The HTML Element ID of the target HTML element that has the pointer lock active.
+ EM_UTF8 id[128];
+} EmscriptenPointerlockChangeEvent;
+
+/*
+ * Registers a callback function for receiving the pointerlockchange event.
+ * Pointer lock hides the mouse cursor and exclusively gives the target element relative mouse movement events via the mousemove event.
+ * http://www.w3.org/TR/pointerlock/
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData));
+/*
+ * Returns the current page pointerlock state.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_pointerlock_status(EmscriptenPointerlockChangeEvent *pointerlockStatus);
+/*
+ * Requests the given target element to grab pointerlock.
+ * Note: This function can only run inside a user-generated JavaScript event handler.
+ * deferUntilInEventHandler: If true and you called this function outside an event callback, this request will
+ * be queued to be executed the next time a JS event handler runs. If false, this
+ * function will instead fail if not running inside a JS event handler.
+ */
+extern EMSCRIPTEN_RESULT emscripten_request_pointerlock(const char *target, int deferUntilInEventHandler);
+/*
+ * Exits pointer lock state and restores the mouse cursor to be visible again.
+ */
+extern EMSCRIPTEN_RESULT emscripten_exit_pointerlock();
+
+#define EMSCRIPTEN_VISIBILITY_HIDDEN 0
+#define EMSCRIPTEN_VISIBILITY_VISIBLE 1
+#define EMSCRIPTEN_VISIBILITY_PRERENDER 2
+#define EMSCRIPTEN_VISIBILITY_UNLOADED 3
+
+/*
+ * The event structure passed in the visibilitychange event.
+ * http://www.w3c-test.org/webperf/specs/PageVisibility/
+ */
+typedef struct EmscriptenVisibilityChangeEvent {
+ // If true, the current browser page is now hidden.
+ EM_BOOL hidden;
+ // Specifies a more fine-grained state of the current page visibility status. One of the EMSCRIPTEN_VISIBILITY_ values.
+ int visibilityState;
+} EmscriptenVisibilityChangeEvent;
+
+/*
+ * Registers a callback function for receiving the visibilitychange event.
+ * http://www.w3c-test.org/webperf/specs/PageVisibility/
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData));
+/*
+ * Returns the current page visibility state.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_visibility_status(EmscriptenVisibilityChangeEvent *visibilityStatus);
+
+/*
+ * Specifies the status of a single touch point on the page.
+ * See http://www.w3.org/TR/touch-events/#touch-interface
+ */
+typedef struct EmscriptenTouchPoint
+{
+ // An identification number for each touch point.
+ long identifier;
+ // The touch coordinate relative to the whole screen origin, in pixels.
+ long screenX;
+ long screenY;
+ // The touch coordinate relative to the viewport, in pixels.
+ long clientX;
+ long clientY;
+ // The touch coordinate relative to the viewport, in pixels, and including any scroll offset.
+ long pageX;
+ long pageY;
+ // Specifies whether this touch point changed during this event.
+ EM_BOOL isChanged;
+ // Specifies whether this touch point is still above the original target on which it was initially pressed against.
+ EM_BOOL onTarget;
+ // The touch coordinates mapped to the Emscripten canvas client area, in pixels.
+ long canvasX;
+ long canvasY;
+} EmscriptenTouchPoint;
+
+/*
+ * Specifies the data of a single touch event.
+ * See http://www.w3.org/TR/touch-events/#touchevent-interface
+ */
+typedef struct EmscriptenTouchEvent {
+ // The number of valid elements in the touches array.
+ int numTouches;
+ // Specifies which modifiers were active during the key event.
+ EM_BOOL ctrlKey;
+ EM_BOOL shiftKey;
+ EM_BOOL altKey;
+ EM_BOOL metaKey;
+ // An array of currently active touches, one for each finger.
+ EmscriptenTouchPoint touches[32];
+} EmscriptenTouchEvent;
+
+/*
+ * Registers a callback function for receiving the touchstart, touchend, touchmove and touchcancel events.
+ * http://www.w3.org/TR/touch-events/
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_touchcancel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData));
+
+/*
+ * Represents the current snapshot state of a gamepad.
+ * http://www.w3.org/TR/gamepad/#gamepad-interface
+ */
+typedef struct EmscriptenGamepadEvent {
+ // Absolute wallclock time in msec units of when the data was recorded.
+ double timestamp;
+ // The number of valid axes entries in the axis array.
+ int numAxes;
+ // The number of valid button entries in the analogButton and digitalButton arrays.
+ int numButtons;
+ // The analog state of the gamepad axes, in the range [-1, 1].
+ double axis[64];
+ // The analog state of the gamepad buttons, in the range [0, 1].
+ double analogButton[64];
+ // The digital state of the gamepad buttons, either 0 or 1.
+ EM_BOOL digitalButton[64];
+ // Specifies whether this gamepad is connected to the browser page.
+ EM_BOOL connected;
+ // An ordinal associated with this gamepad, zero-based.
+ long index;
+ // An ID for the brand or style of the connected gamepad device. Typically, this will include the USB vendor and a product ID.
+ EM_UTF8 id[64];
+ // A string that identifies the layout or control mapping of this device.
+ EM_UTF8 mapping[64];
+} EmscriptenGamepadEvent;
+
+/*
+ * Registers a callback function for receiving the gamepadconnected and gamepaddisconnected events.
+ * http://www.w3.org/TR/gamepad/
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData));
+
+/*
+ * Returns the number of gamepads connected to the system or EMSCRIPTEN_RESULT_NOT_SUPPORTED if the current browser does not support gamepads.
+ * Note: A gamepad does not show up as connected until a button on it is pressed.
+ */
+extern int emscripten_get_num_gamepads();
+/*
+ * Returns a snapshot of the current gamepad state.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_gamepad_status(int index, EmscriptenGamepadEvent *gamepadState);
+
+/*
+ * The event structure passed in the battery chargingchange and levelchange event.
+ * http://www.w3.org/TR/battery-status/#batterymanager-interface
+ */
+typedef struct EmscriptenBatteryEvent {
+ double chargingTime;
+ double dischargingTime;
+ double level;
+ EM_BOOL charging;
+} EmscriptenBatteryEvent;
+
+/*
+ * Registers a callback function for receiving the battery chargingchange and levelchange events.
+ * http://www.w3.org/TR/battery-status/
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_batterychargingchange_callback(void *userData, EM_BOOL (*func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_batterylevelchange_callback(void *userData, EM_BOOL (*func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData));
+/*
+ * Returns the current battery status.
+ */
+extern EMSCRIPTEN_RESULT emscripten_get_battery_status(EmscriptenBatteryEvent *batteryState);
+
+/*
+ * Produces a vibration feedback for given msecs.
+ * http://dev.w3.org/2009/dap/vibration/
+ */
+extern EMSCRIPTEN_RESULT emscripten_vibrate(int msecs);
+/*
+ * Produces a complex vibration feedback pattern.
+ * msecsArray: An array of timing entries [on, off, on, off, on, off, ...] where every second one specifies a duration of vibration, and
+ * every other one specifies a duration of silence.
+ * numEntries: The number of integers in the array msecsArray.
+ */
+extern EMSCRIPTEN_RESULT emscripten_vibrate_pattern(int *msecsArray, int numEntries);
+
+/*
+ * Registers a callback function for receiving the page beforeunload event.
+ * Hook onto this event to perform process right prior to page close, and/or display a confirmation notification asking if the user really wants to leave the page.
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#beforeunloadevent
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_beforeunload_callback(void *userData, const char *(*func)(int eventType, const void *reserved, void *userData));
+
+/*
+ * Registers a callback function for the canvas webgl context webglcontextlost and webglcontextrestored events.
+ * See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ */
+extern EMSCRIPTEN_RESULT emscripten_set_webglcontextlost_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const void *reserved, void *userData));
+extern EMSCRIPTEN_RESULT emscripten_set_webglcontextrestored_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const void *reserved, void *userData));
+
+#ifdef __cplusplus
+} // ~extern "C"
+#endif
+
+#endif
diff --git a/tests/box2d/Benchmark.cpp b/tests/box2d/Benchmark.cpp
index 0db1d4be..4fd79651 100644
--- a/tests/box2d/Benchmark.cpp
+++ b/tests/box2d/Benchmark.cpp
@@ -25,6 +25,10 @@ typedef struct {
#include <time.h>
#include <math.h>
+#if EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
#include "Box2D/Box2D.h"
using namespace std;
@@ -52,6 +56,14 @@ result_t measure(clock_t *times) {
return r;
}
+b2World *world;
+clock_t *times, minn = CLOCKS_PER_SEC * 1000 * 100, maxx = -1;
+b2Body* topBody;
+int32 frameCounter = 0;
+int responsive_main_loop;
+
+void iter();
+
int main(int argc, char **argv) {
int arg = argc > 1 ? argv[1][0] - '0' : 3;
switch(arg) {
@@ -64,24 +76,28 @@ int main(int argc, char **argv) {
default: printf("error: %d\\n", arg); return -1;
}
+ // do not split out warmup, do not ignore initial stalls
+ FRAMES += WARMUP;
+ WARMUP = 0;
+
+ times = new clock_t[FRAMES];
+
// Define the gravity vector.
b2Vec2 gravity(0.0f, -10.0f);
// Construct a world object, which will hold and simulate the rigid bodies.
- b2World world(gravity);
- world.SetAllowSleeping(false);
+ world = new b2World(gravity);
+ world->SetAllowSleeping(false);
{
b2BodyDef bd;
- b2Body* ground = world.CreateBody(&bd);
+ b2Body* ground = world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
- b2Body* topBody;
-
{
float32 a = 0.5f;
b2PolygonShape shape;
@@ -99,7 +115,7 @@ int main(int argc, char **argv) {
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
- b2Body* body = world.CreateBody(&bd);
+ b2Body* body = world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
topBody = body;
@@ -112,25 +128,54 @@ int main(int argc, char **argv) {
}
for (int32 i = 0; i < WARMUP; ++i) {
- world.Step(1.0f/60.0f, 3, 3);
+ world->Step(1.0f/60.0f, 3, 3);
+ }
+
+#if EMSCRIPTEN
+ responsive_main_loop = argc > 2 ? argv[2][0] - '0' : 0;
+ if (responsive_main_loop) {
+ printf("responsive main loop\n");
+ emscripten_set_main_loop(iter, 60, 1);
+ } else {
+#endif
+ do {
+ iter();
+ } while (frameCounter <= FRAMES);
+#if EMSCRIPTEN
}
+#endif
- clock_t times[FRAMES];
- for (int32 i = 0; i < FRAMES; ++i) {
- clock_t start = clock();
- world.Step(1.0f/60.0f, 3, 3);
- clock_t end = clock();
- times[i] = end - start;
+ return 0;
+}
+
+void iter() {
+ if (frameCounter < FRAMES) {
+ clock_t start = clock();
+ world->Step(1.0f/60.0f, 3, 3);
+ clock_t end = clock();
+ clock_t curr = end - start;
+ times[frameCounter] = curr;
+ if (curr < minn) minn = curr;
+ if (curr > maxx) maxx = curr;
#if DEBUG
printf("%f :: ", topBody->GetPosition().y);
- printf("%f\n", (float32)(end - start) / CLOCKS_PER_SEC * 1000);
+ printf("%f\n", (float32)(end - start) / CLOCKS_PER_SEC * 1000);
#endif
- }
+ frameCounter++;
+ return;
+ }
+
+ // that's it!
+
+ frameCounter++;
result_t result = measure(times);
- printf("frame averages: %.3f +- %.3f\n", result.mean, result.stddev);
+ printf("frame averages: %.3f +- %.3f, range: %.3f to %.3f \n", result.mean, result.stddev, float(minn)/CLOCKS_PER_SEC * 1000, float(maxx)/CLOCKS_PER_SEC * 1000);
- return 0;
+#if EMSCRIPTEN
+ emscripten_run_script("if (Module.reportCompletion) Module.reportCompletion()");
+ if (responsive_main_loop) emscripten_cancel_main_loop();
+#endif
}
diff --git a/tests/sqlite/benchmark.c b/tests/sqlite/benchmark.c
index 802abab1..de800742 100644
--- a/tests/sqlite/benchmark.c
+++ b/tests/sqlite/benchmark.c
@@ -2,8 +2,13 @@
#include <stdio.h>
#include <sqlite3.h>
+#include <emscripten.h>
+
+int print = 1;
+
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
+ if (!print) return 0;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
@@ -44,11 +49,15 @@ int test(){
return 0;
}
-int main(){
+int main(int argc, char **argv){
sqlite3 *db;
char *zErrMsg = 0;
int rc, i;
clock_t t;
+ int n, m;
+
+ n = argc > 1 ? atoi(argv[1]) : 5000;
+ m = argc > 2 ? atoi(argv[2]) : 1;
rc = sqlite3_open(":memory:", &db);
if( rc ){
@@ -69,20 +78,21 @@ int main(){
#define TIME(msg) \
{ \
- printf(msg " : took %d ms\n", (1000*(clock()-t))/CLOCKS_PER_SEC); \
- t = clock(); \
+ int now = emscripten_get_now(); \
+ printf(msg " : took %d ms\n", (int)(now - t)); \
+ t = now; \
}
t = clock();
- TIME("'startup'");
+ TIME("'startup' - IGNORE THIS VALUE, it is an artifact");
RUN("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));");
TIME("create table");
RUN("BEGIN;");
- // 25000 INSERTs in a transaction
- for (i = 0; i < 5000; i++) {
+ // n*5 INSERTs in a transaction
+ for (i = 0; i < n; i++) {
RUN("INSERT INTO t1 VALUES(1,12345,'one 1 one 1 one 1');");
RUN("INSERT INTO t1 VALUES(2,23422,'two two two two');");
RUN("INSERT INTO t1 VALUES(3,31233,'three three 33333333333 three');");
@@ -95,10 +105,13 @@ int main(){
TIME("commit");
// Counts
- RUN("SELECT count(*) FROM t1;");
- RUN("SELECT count(*) FROM t1 WHERE a == 4");
- RUN("SELECT count(*) FROM t1 WHERE b > 20000 AND b < 50000;");
- RUN("SELECT count(*) FROM t1 WHERE c like '%three%';");
+ for (i = 0; i < m; i++) {
+ print = i == 0;
+ RUN("SELECT count(*) FROM t1;");
+ RUN("SELECT count(*) FROM t1 WHERE a == 4");
+ RUN("SELECT count(*) FROM t1 WHERE b > 20000 AND b < 50000;");
+ RUN("SELECT count(*) FROM t1 WHERE c like '%three%';");
+ }
TIME("selects");
// Index
@@ -106,8 +119,11 @@ int main(){
RUN("CREATE INDEX iibb ON t1(b);");
TIME("create indexes");
- RUN("SELECT count(*) FROM t1 WHERE a == 4");
- RUN("SELECT count(*) FROM t1 WHERE b > 20000 AND b < 50000;");
+ for (i = 0; i < m; i++) {
+ print = i == 0;
+ RUN("SELECT count(*) FROM t1 WHERE a == 4");
+ RUN("SELECT count(*) FROM t1 WHERE b > 20000 AND b < 50000;");
+ }
TIME("selects with indexes");
sqlite3_close(db);
diff --git a/tests/test_browser.py b/tests/test_browser.py
index 3000240d..f185c211 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -1813,3 +1813,5 @@ Module["preRun"].push(function () {
self.btest('doublestart.c', args=['--pre-js', 'pre.js', '-o', 'test.html'], expected='1')
+ def test_html5(self):
+ self.btest(path_from_root('tests', 'test_html5.c'), expected='0')
diff --git a/tests/test_core.py b/tests/test_core.py
index d25847d7..97cba68f 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -2809,6 +2809,46 @@ def process(filename):
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
post_build=self.dlfcn_post_build)
+ def test_dlfcn_i64(self):
+ if not self.can_dlfcn(): return
+ if not Settings.ASM_JS: return self.skip('TODO')
+
+ self.prep_dlfcn_lib()
+ Settings.EXPORTED_FUNCTIONS = ['_foo']
+ lib_src = '''
+ int foo(int x) {
+ return (long long)x / (long long)1234;
+ }
+ '''
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ Settings.EXPORTED_FUNCTIONS = ['_main']
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <dlfcn.h>
+
+ typedef int (*intfunc)(int);
+
+ void *p;
+
+ int main() {
+ p = malloc(1024);
+ void *lib_handle = dlopen("liblib.so", 0);
+ printf("load %p\n", lib_handle);
+ intfunc x = (intfunc)dlsym(lib_handle, "foo");
+ printf("foo func %p\n", x);
+ if (p == 0) return 1;
+ printf("|%d|\n", x(81234567));
+ return 0;
+ }
+ '''
+ self.do_run(src, '|65830|', post_build=self.dlfcn_post_build)
+
def test_dlfcn_qsort(self):
if not self.can_dlfcn(): return
@@ -6267,6 +6307,9 @@ def process(filename):
self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*')
Settings.CORRECT_SIGNS = 0
+ def test_float_literals(self):
+ self.do_run_from_file(path_from_root('tests', 'test_float_literals.cpp'), path_from_root('tests', 'test_float_literals.out'))
+
def test_exit_status(self):
if self.emcc_args is None: return self.skip('need emcc')
src = r'''
diff --git a/tests/test_float_literals.cpp b/tests/test_float_literals.cpp
new file mode 100644
index 00000000..fdae2764
--- /dev/null
+++ b/tests/test_float_literals.cpp
@@ -0,0 +1,92 @@
+#include <limits>
+#include <math.h>
+#include <float.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) || defined(EMSCRIPTEN)
+#define FLOAT_NAN ((float)std::numeric_limits<float>::quiet_NaN())
+#define FLOAT_INF ((float)std::numeric_limits<float>::infinity())
+#else
+#define FLOAT_NAN ((float)NAN)
+#define FLOAT_INF ((float)INFINITY)
+#endif
+
+#if defined(_MSC_VER) || defined(EMSCRIPTEN)
+#define DOUBLE_NAN ((double)std::numeric_limits<double>::quiet_NaN())
+#define DOUBLE_INF ((double)std::numeric_limits<double>::infinity())
+#else
+#define DOUBLE_NAN ((double)NAN)
+#define DOUBLE_INF ((double)INFINITY)
+#endif
+
+#ifdef _MSC_VER
+#define NOINLINE
+#else
+#define NOINLINE __attribute__((noinline))
+#endif
+
+float NOINLINE ret_e() { return (float)2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; }
+float NOINLINE ret_minuspi() { return (float)-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; }
+float NOINLINE val() { return 10.f; }
+float NOINLINE val2() { return -10.f; }
+float NOINLINE zero() { return 0.f; }
+float NOINLINE zero2() { return -0.f; }
+
+double NOINLINE dret_e() { return (double)2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; }
+double NOINLINE dret_minuspi() { return (double)-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; }
+double NOINLINE dval() { return 10.0; }
+double NOINLINE dval2() { return -10.0; }
+double NOINLINE dzero() { return 0.0; }
+double NOINLINE dzero2() { return -0.0; }
+
+const float e = ret_e();
+const float negpi = ret_minuspi();
+const float inf = FLOAT_INF;
+const float negInf = -FLOAT_INF;
+const float floatNan = FLOAT_NAN;
+const float floatMax = FLT_MAX;
+const float floatMin = -FLT_MAX;
+const float posValue = val();
+const float negValue = val2();
+const float posZero = zero();
+const float negZero = zero2();
+
+const double de = dret_e();
+const double dnegpi = dret_minuspi();
+const double dinf = DOUBLE_INF;
+const double dnegInf = -DOUBLE_INF;
+const double doubleNan = DOUBLE_NAN;
+const double doubleMax = DBL_MAX;
+const double doubleMin = -DBL_MAX;
+const double dposValue = dval();
+const double dnegValue = dval2();
+const double dposZero = dzero();
+const double dnegZero = dzero2();
+
+int main()
+{
+ printf("e: %f\n", e);
+ printf("negpi: %f\n", negpi);
+ printf("inf: %f\n", inf);
+ printf("negInf: %f\n", negInf);
+ printf("floatNan: %f\n", floatNan);
+ printf("floatMax: %f\n", floatMax);
+ printf("floatMin: %f\n", floatMin);
+ printf("posValue: %f\n", posValue);
+ printf("negValue: %f\n", negValue);
+ printf("posZero: %f\n", posZero);
+ printf("negZero: %f\n", negZero);
+
+ printf("e: %f\n", de);
+ printf("negpi: %f\n", dnegpi);
+ printf("inf: %f\n", dinf);
+ printf("negInf: %f\n", dnegInf);
+ printf("doubleNan: %f\n", doubleNan);
+ printf("doubleMax: %f\n", doubleMax);
+ printf("doubleMin: %f\n", doubleMin);
+ printf("posValue: %f\n", dposValue);
+ printf("negValue: %f\n", dnegValue);
+ printf("posZero: %f\n", dposZero);
+ printf("negZero: %f\n", dnegZero);
+}
diff --git a/tests/test_float_literals.out b/tests/test_float_literals.out
new file mode 100644
index 00000000..ab52d6c4
--- /dev/null
+++ b/tests/test_float_literals.out
@@ -0,0 +1,22 @@
+e: 2.718282
+negpi: -3.141593
+inf: inf
+negInf: -inf
+floatNan: nan
+floatMax: 3.4028234663852886e+38
+floatMin: -3.4028234663852886e+38
+posValue: 10.000000
+negValue: -10.000000
+posZero: 0.000000
+negZero: -0.000000
+e: 2.718282
+negpi: -3.141593
+inf: inf
+negInf: -inf
+doubleNan: nan
+doubleMax: 1.7976931348623157e+308
+doubleMin: -1.7976931348623157e+308
+posValue: 10.000000
+negValue: -10.000000
+posZero: 0.000000
+negZero: -0.000000
diff --git a/tests/test_html5.c b/tests/test_html5.c
new file mode 100644
index 00000000..77ddea98
--- /dev/null
+++ b/tests/test_html5.c
@@ -0,0 +1,474 @@
+#include <stdio.h>
+#include <emscripten.h>
+#include <string.h>
+#include <emscripten/html5.h>
+
+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",
+ "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload",
+ "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" };
+ ++eventType;
+ if (eventType < 0) eventType = 0;
+ if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1;
+ return events[eventType];
+}
+
+const char *emscripten_result_to_string(EMSCRIPTEN_RESULT result) {
+ if (result == EMSCRIPTEN_RESULT_SUCCESS) return "EMSCRIPTEN_RESULT_SUCCESS";
+ if (result == EMSCRIPTEN_RESULT_DEFERRED) return "EMSCRIPTEN_RESULT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_NOT_SUPPORTED) return "EMSCRIPTEN_RESULT_NOT_SUPPORTED";
+ if (result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED) return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED";
+ if (result == EMSCRIPTEN_RESULT_INVALID_TARGET) return "EMSCRIPTEN_RESULT_INVALID_TARGET";
+ if (result == EMSCRIPTEN_RESULT_UNKNOWN_TARGET) return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET";
+ if (result == EMSCRIPTEN_RESULT_INVALID_PARAM) return "EMSCRIPTEN_RESULT_INVALID_PARAM";
+ if (result == EMSCRIPTEN_RESULT_FAILED) return "EMSCRIPTEN_RESULT_FAILED";
+ if (result == EMSCRIPTEN_RESULT_NO_DATA) return "EMSCRIPTEN_RESULT_NO_DATA";
+ return "Unknown EMSCRIPTEN_RESULT!";
+}
+
+#define TEST_RESULT(x) if (ret != EMSCRIPTEN_RESULT_SUCCESS) printf("%s returned %s.\n", #x, emscripten_result_to_string(ret));
+
+// The event handler functions can return 1 to suppress the event and disable the default action. That calls event.preventDefault();
+// Returning 0 signals that the event was not consumed by the code, and will allow the event to pass on and bubble up normally.
+EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *e, void *userData)
+{
+ printf("%s, key: \"%s\", code: \"%s\", location: %lu,%s%s%s%s repeat: %d, locale: \"%s\", char: \"%s\", charCode: %lu, keyCode: %lu, which: %lu\n",
+ emscripten_event_type_to_string(eventType), e->key, e->code, e->location,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "",
+ e->repeat, e->locale, e->charValue, e->charCode, e->keyCode, e->which);
+
+ if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "f") || e->which == 102)) {
+ EmscriptenFullscreenChangeEvent fsce;
+ EMSCRIPTEN_RESULT ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (!fsce.isFullscreen) {
+ printf("Requesting fullscreen..\n");
+ ret = emscripten_request_fullscreen(0, 1);
+ TEST_RESULT(emscripten_request_fullscreen);
+ } else {
+ printf("Exiting fullscreen..\n");
+ ret = emscripten_exit_fullscreen();
+ TEST_RESULT(emscripten_exit_fullscreen);
+ ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (fsce.isFullscreen) {
+ fprintf(stderr, "Fullscreen exit did not work!\n");
+ }
+ }
+ }
+
+ if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "p") || e->which == 112)) {
+ EmscriptenPointerlockChangeEvent plce;
+ EMSCRIPTEN_RESULT ret = emscripten_get_pointerlock_status(&plce);
+ TEST_RESULT(emscripten_get_pointerlock_status);
+ if (!plce.isActive) {
+ printf("Requesting pointer lock..\n");
+ ret = emscripten_request_pointerlock(0, 1);
+ TEST_RESULT(emscripten_request_pointerlock);
+ } else {
+ printf("Exiting pointer lock..\n");
+ ret = emscripten_exit_pointerlock();
+ TEST_RESULT(emscripten_exit_pointerlock);
+ ret = emscripten_get_pointerlock_status(&plce);
+ TEST_RESULT(emscripten_get_pointerlock_status);
+ if (plce.isActive) {
+ fprintf(stderr, "Pointer lock exit did not work!\n");
+ }
+ }
+ }
+
+ return 0;
+}
+
+EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld)\n",
+ emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "",
+ e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY);
+
+ return 0;
+}
+
+EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent *e, void *userData)
+{
+ printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), delta:(%g,%g,%g), deltaMode:%lu\n",
+ emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY,
+ e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "",
+ e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY,
+ (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode);
+
+ return 0;
+}
+
+EM_BOOL uievent_callback(int eventType, const EmscriptenUiEvent *e, void *userData)
+{
+ printf("%s, detail: %ld, document.body.client size: (%d,%d), window.inner size: (%d,%d), scrollPos: (%d, %d)\n",
+ emscripten_event_type_to_string(eventType), e->detail, e->documentBodyClientWidth, e->documentBodyClientHeight,
+ e->windowInnerWidth, e->windowInnerHeight, e->scrollTop, e->scrollLeft);
+
+ return 0;
+}
+
+EM_BOOL focusevent_callback(int eventType, const EmscriptenFocusEvent *e, void *userData)
+{
+ printf("%s, nodeName: \"%s\", id: \"%s\"\n", emscripten_event_type_to_string(eventType), e->nodeName, e->id[0] == '\0' ? "(empty string)" : e->id);
+
+ return 0;
+}
+
+EM_BOOL deviceorientation_callback(int eventType, const EmscriptenDeviceOrientationEvent *e, void *userData)
+{
+ printf("%s, (%g, %g, %g)\n", emscripten_event_type_to_string(eventType), e->alpha, e->beta, e->gamma);
+
+ return 0;
+}
+
+EM_BOOL devicemotion_callback(int eventType, const EmscriptenDeviceMotionEvent *e, void *userData)
+{
+ printf("%s, accel: (%g, %g, %g), accelInclGravity: (%g, %g, %g), rotationRate: (%g, %g, %g)\n",
+ emscripten_event_type_to_string(eventType),
+ e->accelerationX, e->accelerationY, e->accelerationZ,
+ e->accelerationIncludingGravityX, e->accelerationIncludingGravityY, e->accelerationIncludingGravityZ,
+ e->rotationRateAlpha, e->rotationRateBeta, e->rotationRateGamma);
+
+ return 0;
+}
+
+EM_BOOL orientationchange_callback(int eventType, const EmscriptenOrientationChangeEvent *e, void *userData)
+{
+ printf("%s, orientationAngle: %d, orientationIndex: %d\n", emscripten_event_type_to_string(eventType), e->orientationAngle, e->orientationIndex);
+
+ return 0;
+}
+
+EM_BOOL fullscreenchange_callback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
+{
+ printf("%s, isFullscreen: %d, fullscreenEnabled: %d, fs element nodeName: \"%s\", fs element id: \"%s\"\n",
+ emscripten_event_type_to_string(eventType), e->isFullscreen, e->fullscreenEnabled, e->nodeName, e->id);
+
+ return 0;
+}
+
+EM_BOOL pointerlockchange_callback(int eventType, const EmscriptenPointerlockChangeEvent *e, void *userData)
+{
+ printf("%s, isActive: %d, pointerlock element nodeName: \"%s\", id: \"%s\"\n",
+ emscripten_event_type_to_string(eventType), e->isActive, e->nodeName, e->id);
+
+ return 0;
+}
+
+EM_BOOL visibilitychange_callback(int eventType, const EmscriptenVisibilityChangeEvent *e, void *userData)
+{
+ printf("%s, hidden: %d, visibilityState: %d\n", emscripten_event_type_to_string(eventType), e->hidden, e->visibilityState);
+
+ return 0;
+}
+
+EM_BOOL touch_callback(int eventType, const EmscriptenTouchEvent *e, void *userData)
+{
+ printf("%s, numTouches: %d %s%s%s%s\n",
+ emscripten_event_type_to_string(eventType), e->numTouches,
+ e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "");
+ for(int i = 0; i < e->numTouches; ++i)
+ {
+ const EmscriptenTouchPoint *t = &e->touches[i];
+ printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n",
+ t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY);
+ }
+
+ return 0;
+}
+
+EM_BOOL gamepad_callback(int eventType, const EmscriptenGamepadEvent *e, void *userData)
+{
+ printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n",
+ eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", e->timestamp, e->connected, e->index,
+ e->numAxes, e->numButtons, e->id, e->mapping);
+
+ if (e->connected)
+ {
+ for(int i = 0; i < e->numAxes; ++i)
+ printf("Axis %d: %g\n", i, e->axis[i]);
+
+ for(int i = 0; i < e->numButtons; ++i)
+ printf("Button %d: Digital: %d, Analog: %g\n", i, e->digitalButton[i], e->analogButton[i]);
+ }
+
+ return 0;
+}
+
+const char *beforeunload_callback(int eventType, const void *reserved, void *userData)
+{
+#ifdef REPORT_RESULT
+ return ""; // For test harness, don't show a confirmation dialog to not block and keep the test runner automated.
+#else
+ return "Do you really want to leave the page?";
+#endif
+}
+
+void formatTime(char *str, int seconds)
+{
+ int h = seconds / (60*60);
+ seconds -= h*60*60;
+ int m = seconds / 60;
+ seconds -= m*60;
+ if (h > 0)
+ {
+ sprintf(str, "%dh:%02dm:%02ds", h, m, seconds);
+ }
+ else
+ {
+ sprintf(str, "%02dm:%02ds", m, seconds);
+ }
+}
+
+EM_BOOL battery_callback(int eventType, const EmscriptenBatteryEvent *e, void *userData)
+{
+ char t1[64];
+ formatTime(t1, (int)e->chargingTime);
+ char t2[64];
+ formatTime(t2, (int)e->dischargingTime);
+ printf("%s: chargingTime: %s, dischargingTime: %s, level: %g%%, charging: %d\n",
+ emscripten_event_type_to_string(eventType), t1, t2, e->level*100, e->charging);
+
+ return 0;
+}
+
+EM_BOOL webglcontext_callback(int eventType, const void *reserved, void *userData)
+{
+ printf("%s.\n", emscripten_event_type_to_string(eventType));
+
+ return 0;
+}
+
+EmscriptenGamepadEvent prevState[32];
+int prevNumGamepads = 0;
+
+void mainloop()
+{
+ int numGamepads = emscripten_get_num_gamepads();
+ if (numGamepads != prevNumGamepads)
+ {
+ if (numGamepads == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
+ printf("emscripten_get_num_gamepads returned EMSCRIPTEN_RESULT_NOT_SUPPORTED.\n");
+ emscripten_cancel_main_loop();
+ return;
+ } else {
+ printf("Number of connected gamepads: %d\n", numGamepads);
+ }
+ prevNumGamepads = numGamepads;
+ }
+
+ for(int i = 0; i < numGamepads && i < 32; ++i)
+ {
+ EmscriptenGamepadEvent ge;
+ int failed = emscripten_get_gamepad_status(i, &ge);
+ if (!failed)
+ {
+ int g = ge.index;
+ for(int j = 0; j < ge.numAxes; ++j)
+ {
+ if (ge.axis[j] != prevState[g].axis[j])
+ printf("Gamepad %d, axis %d: %g\n", g, j, ge.axis[j]);
+ }
+
+ for(int j = 0; j < ge.numButtons; ++j)
+ {
+ if (ge.analogButton[j] != prevState[g].analogButton[j] || ge.digitalButton[j] != prevState[g].digitalButton[j])
+ printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", g, j, ge.digitalButton[j], ge.analogButton[j]);
+ }
+ prevState[g] = ge;
+ }
+ }
+
+}
+
+#ifdef REPORT_RESULT
+void report_result(void *arg)
+{
+ int result = 0;
+ REPORT_RESULT();
+}
+#endif
+
+int main()
+{
+
+ EMSCRIPTEN_RESULT ret = emscripten_set_keypress_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keypress_callback);
+ ret = emscripten_set_keydown_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keydown_callback);
+ ret = emscripten_set_keyup_callback(0, 0, 1, key_callback);
+ TEST_RESULT(emscripten_set_keyup_callback);
+
+ ret = emscripten_set_click_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_click_callback);
+ ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousedown_callback);
+ ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mouseup_callback);
+ ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_dblclick_callback);
+ ret = emscripten_set_mousemove_callback(0, 0, 1, mouse_callback);
+ TEST_RESULT(emscripten_set_mousemove_callback);
+
+ ret = emscripten_set_wheel_callback(0, 0, 1, wheel_callback);
+ TEST_RESULT(emscripten_set_wheel_callback);
+
+ ret = emscripten_set_resize_callback(0, 0, 1, uievent_callback);
+ TEST_RESULT(emscripten_set_resize_callback);
+ ret = emscripten_set_scroll_callback(0, 0, 1, uievent_callback);
+ TEST_RESULT(emscripten_set_scroll_callback);
+
+ ret = emscripten_set_blur_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_blur_callback);
+ ret = emscripten_set_focus_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_focus_callback);
+ ret = emscripten_set_focusin_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_focusin_callback);
+ ret = emscripten_set_focusout_callback(0, 0, 1, focusevent_callback);
+ TEST_RESULT(emscripten_set_focusout_callback);
+
+ ret = emscripten_set_deviceorientation_callback(0, 1, deviceorientation_callback);
+ TEST_RESULT(emscripten_set_deviceorientation_callback);
+ ret = emscripten_set_devicemotion_callback(0, 1, devicemotion_callback);
+ TEST_RESULT(emscripten_set_devicemotion_callback);
+
+ ret = emscripten_set_orientationchange_callback(0, 1, orientationchange_callback);
+ TEST_RESULT(emscripten_set_orientationchange_callback);
+
+ // Test the polling of orientation.
+ EmscriptenOrientationChangeEvent oce;
+ ret = emscripten_get_orientation_status(&oce);
+ TEST_RESULT(emscripten_get_orientation_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current orientation is:\n");
+ orientationchange_callback(EMSCRIPTEN_EVENT_ORIENTATIONCHANGE, &oce, 0);
+ }
+
+ int newOrientation = (oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY
+ || oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY) ? EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY : EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY;
+ // Test locking of orientation.
+ ret = emscripten_lock_orientation(newOrientation);
+ TEST_RESULT(emscripten_lock_orientation);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Locked orientation to state %d.\n", newOrientation);
+ }
+
+ ret = emscripten_get_orientation_status(&oce);
+ TEST_RESULT(emscripten_get_orientation_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current orientation is after locking:\n");
+ orientationchange_callback(18, &oce, 0);
+ }
+
+ ret = emscripten_unlock_orientation();
+ TEST_RESULT(emscripten_unlock_orientation);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Unlocked orientation.\n");
+ }
+
+ EmscriptenFullscreenChangeEvent fsce;
+ ret = emscripten_get_fullscreen_status(&fsce);
+ TEST_RESULT(emscripten_get_fullscreen_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current fullscreen status is:\n");
+ fullscreenchange_callback(EMSCRIPTEN_EVENT_FULLSCREENCHANGE, &fsce, 0);
+ }
+
+ ret = emscripten_set_fullscreenchange_callback(0, 0, 1, fullscreenchange_callback);
+ TEST_RESULT(emscripten_set_fullscreenchange_callback);
+
+ // These won't do anything, since fullscreen must be requested in an event handler,
+ // but call these anyways to confirm that they don't crash in an exception in the test suite.
+ ret = emscripten_request_fullscreen(0, 1);
+ TEST_RESULT(emscripten_request_fullscreen);
+ ret = emscripten_exit_fullscreen();
+ TEST_RESULT(emscripten_exit_fullscreen);
+
+ EmscriptenPointerlockChangeEvent plce;
+ ret = emscripten_get_pointerlock_status(&plce);
+ TEST_RESULT(emscripten_get_pointerlock_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("The current pointerlock status is:\n");
+ pointerlockchange_callback(EMSCRIPTEN_EVENT_POINTERLOCKCHANGE, &plce, 0);
+ }
+
+ ret = emscripten_set_pointerlockchange_callback(0, 0, 1, pointerlockchange_callback);
+ TEST_RESULT(emscripten_set_pointerlockchange_callback);
+
+ // These won't do anything, since pointer lock must be requested in an event handler,
+ // but call these anyways to confirm that they don't crash in an exception in the test suite.
+ ret = emscripten_request_pointerlock(0, 1);
+ TEST_RESULT(emscripten_request_pointerlock);
+ ret = emscripten_exit_pointerlock();
+ TEST_RESULT(emscripten_exit_pointerlock);
+
+ int vibratePattern[] = {
+ 150, 500,
+ 300, 500,
+ 450
+ };
+ ret = emscripten_vibrate_pattern(vibratePattern, sizeof(vibratePattern)/sizeof(vibratePattern[0]));
+ TEST_RESULT(emscripten_vibrate_pattern);
+
+ EmscriptenVisibilityChangeEvent vce;
+ ret = emscripten_get_visibility_status(&vce);
+ TEST_RESULT(emscripten_get_visibility_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Current visibility status:\n");
+ visibilitychange_callback(EMSCRIPTEN_EVENT_VISIBILITYCHANGE, &vce, 0);
+ }
+
+ ret = emscripten_set_visibilitychange_callback(0, 1, visibilitychange_callback);
+ TEST_RESULT(emscripten_set_visibilitychange_callback);
+
+ ret = emscripten_set_touchstart_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchstart_callback);
+ ret = emscripten_set_touchend_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchend_callback);
+ ret = emscripten_set_touchmove_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchmove_callback);
+ ret = emscripten_set_touchcancel_callback(0, 0, 1, touch_callback);
+ TEST_RESULT(emscripten_set_touchcancel_callback);
+
+ ret = emscripten_set_gamepadconnected_callback(0, 1, gamepad_callback);
+ TEST_RESULT(emscripten_set_gamepadconnected_callback);
+ ret = emscripten_set_gamepaddisconnected_callback(0, 1, gamepad_callback);
+ TEST_RESULT(emscripten_set_gamepaddisconnected_callback);
+
+ emscripten_set_main_loop(mainloop, 10, 0);
+
+ ret = emscripten_set_beforeunload_callback(0, beforeunload_callback);
+ TEST_RESULT(emscripten_set_beforeunload_callback);
+
+ ret = emscripten_set_batterychargingchange_callback(0, battery_callback);
+ TEST_RESULT(emscripten_set_batterychargingchange_callback);
+ ret = emscripten_set_batterylevelchange_callback(0, battery_callback);
+ TEST_RESULT(emscripten_set_batterylevelchange_callback);
+
+ EmscriptenBatteryEvent bs;
+ ret = emscripten_get_battery_status(&bs);
+ TEST_RESULT(emscripten_get_battery_status);
+ if (ret == EMSCRIPTEN_RESULT_SUCCESS) {
+ printf("Current battery status:\n");
+ battery_callback(EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE, &bs, 0);
+ }
+
+ ret = emscripten_set_webglcontextlost_callback(0, 0, 1, webglcontext_callback);
+ TEST_RESULT(emscripten_set_webglcontextlost_callback);
+ ret = emscripten_set_webglcontextrestored_callback(0, 0, 1, webglcontext_callback);
+ TEST_RESULT(emscripten_set_webglcontextrestored_callback);
+
+ /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means.
+ Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */
+ EM_ASM(Module['noExitRuntime'] = true);
+
+#ifdef REPORT_RESULT
+ // Keep the page running for a moment.
+ emscripten_async_call(report_result, 0, 5000);
+#endif
+ return 0;
+}
diff --git a/tests/test_other.py b/tests/test_other.py
index 53128391..8895a911 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -1773,6 +1773,8 @@ f.close()
['asm', 'eliminate']),
(path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(),
['asm', 'registerize']),
+ (path_from_root('tools', 'test-js-optimizer-asm-regs-harder.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-harder-output.js')).read(),
+ ['asm', 'registerizeHarder']),
(path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(),
['asm', 'registerize', 'minifyLocals']),
(path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(),
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index b35da99d..21d521fd 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -2460,55 +2460,40 @@ function registerizeHarder(ast) {
buildFlowGraph(node[2]);
break;
case 'switch':
- // This is horrific. It need to capture the default flow-through
- // logic of sequential case bodies, as well as the theoretical
- // sequential evaluation of each case clause.
- // TODO: simplify based on asmjs switch-statement restrictions.
+ // Emscripten generates switch statements of a very limited
+ // form: all case clauses are numeric literals, and all
+ // case bodies end with a break. So it's basically equivalent
+ // to a multi-way 'if' statement.
isInExpr++;
buildFlowGraph(node[1]);
isInExpr--;
var jCheckExit = markJunction();
var jExit = addJunction();
pushActiveLabels(null, jExit);
- // Process all cases as a sequential chain of checks.
- // Process all case bodies as one big flow-through statement.
- // They might break themselves out of it but this implements the
- // default fall-through case logic.
var hasDefault = false;
- var jPrevCaseExit = jCheckExit;
- var jPrevBodyExit = jCheckExit;
for (var i=0; i<node[2].length; i++) {
- // In the general case we'll need a basic block for the case clause.
- // Try to avoid it for common, simple, non-var-using cases.
+ setJunction(jCheckExit);
if (!node[2][i][0]) {
hasDefault = true;
} else {
if (node[2][i][0][0] !== 'num') {
- setJunction(jPrevCaseExit);
- isInExpr++;
- buildFlowGraph(node[2][i][0]);
- isInExpr--;
- jPrevCaseExit = markJunction();
+ if (node[2][i][0][0] !== 'unary-prefix' || node[2][i][0][2][0] !== 'num') {
+ assert(false, 'non-numeric switch case clause');
+ }
}
- }
- // The next case body flows from the exit of the prev one,
- // or may be entered directly from the case statement exit.
- setJunction(jPrevCaseExit);
- if (jPrevBodyExit !== jCheckExit) {
- joinJunction(jPrevBodyExit);
+ addPreCondTrue(['binary', '==', node[1], node[2][i][0]]);
}
for (var j = 0; j < node[2][i][1].length; j++) {
buildFlowGraph(node[2][i][1][j]);
}
- jPrevBodyExit = markJunction();
+ if (currEntryJunction !== null, 'switch case body did not break');
}
- markJunction(jExit);
// If there was no default case, we also need an empty block
- // linking straight from entry to exit.
- if (!hasDefault && jCheckExit !== jPrevBodyExit) {
+ // linking straight from the test evaluation to the exit.
+ if (!hasDefault) {
setJunction(jCheckExit);
- joinJunction(jExit);
}
+ markJunction(jExit);
popActiveLabels()
break;
case 'return':
@@ -2612,6 +2597,7 @@ function registerizeHarder(ast) {
if (labelCond && labelCond[0] === '==') {
// If there are multiple blocks with the same label, all bets are off.
// This seems to happen sometimes for short blocks that end with a return.
+ // TODO: it should be safe to merge the duplicates if they're identical.
if (labelCond[1] in labelledBlocks) {
labelledBlocks = {};
labelledJumps = [];
@@ -2711,6 +2697,7 @@ function registerizeHarder(ast) {
var link = {};
var lastUseLoc = {};
var firstDeadLoc = {};
+ var firstKillLoc = {};
var lastKillLoc = {};
for (var name in live) {
link[name] = name;
@@ -2735,6 +2722,7 @@ function registerizeHarder(ast) {
delete use[name];
delete live[name];
firstDeadLoc[name] = j;
+ firstKillLoc[name] = j;
if (lastUseLoc[name] === undefined) {
lastUseLoc[name] = j;
}
@@ -2760,6 +2748,9 @@ function registerizeHarder(ast) {
for (var name in lastUseLoc) {
lastUseLoc[name] -= n;
}
+ for (var name in firstKillLoc) {
+ firstKillLoc[name] -= n;
+ }
for (var name in lastKillLoc) {
lastKillLoc[name] -= n;
}
@@ -2791,6 +2782,7 @@ function registerizeHarder(ast) {
block.link = link;
block.lastUseLoc = lastUseLoc;
block.firstDeadLoc = firstDeadLoc;
+ block.firstKillLoc = firstKillLoc;
block.lastKillLoc = lastKillLoc;
}
@@ -2975,47 +2967,41 @@ function registerizeHarder(ast) {
if (block.nodes.length === 0) continue;
var jEnter = junctions[block.entry];
var jExit = junctions[block.exit];
- // Calculate the full internal liveness states for this block.
- var liveness = [{}];
- for (var name in jExit.live) {
- liveness[0][name] = 1;
- }
- for (var j=block.nodes.length-1; j>=0; j--) {
- var node = block.nodes[j];
- liveness.unshift(copy(liveness[0]));
- if (node[0] === 'assign') {
- var name = node[2][1];
- delete liveness[0][name];
- } else if (node[0] === 'name') {
- var name = node[1];
- liveness[0][name] = 1;
- } else {
- assert(false, 'unexpected node type: ' + node[0]);
- }
- }
- assert(liveness.length == block.nodes.length + 1);
- assert(setSize(setSub(liveness[0], jEnter.live)) == 0);
- // Mark the point at which each output reg gets assigned.
- // Variables that live past this point must not be assigned
+ // Mark the point at which each input reg becomes dead.
+ // Variables alive before this point must not be assigned
// to that register.
- var outputAssignLoc = {};
- var outputVars = {};
+ var inputVars = {}
+ var inputDeadLoc = {};
+ var inputVarsByReg = {};
for (var name in jExit.live) {
- var reg = junctionVariables[name].reg;
- assert(reg !== null, 'output variable doesnt have a register');
- outputAssignLoc[reg] = block.lastKillLoc[name];
- outputVars[reg] = name;
- }
- // Scan through in execution order, allocating registers on demand.
- // Be careful to avoid conflicts with the output registers.
+ if (!(name in block.kill)) {
+ inputVars[name] = 1;
+ var reg = junctionVariables[name].reg;
+ assert(reg !== null, 'input variable doesnt have a register');
+ inputDeadLoc[reg] = block.firstDeadLoc[name];
+ inputVarsByReg[reg] = name;
+ }
+ }
+ for (var name in block.use) {
+ if (!(name in inputVars)) {
+ inputVars[name] = 1;
+ var reg = junctionVariables[name].reg;
+ assert(reg !== null, 'input variable doesnt have a register');
+ inputDeadLoc[reg] = block.firstDeadLoc[name];
+ inputVarsByReg[reg] = name;
+ }
+ }
+ assert(setSize(setSub(inputVars, jEnter.live)) == 0);
+ // Scan through backwards, allocating registers on demand.
+ // Be careful to avoid conflicts with the input registers.
// We consume free registers in last-used order, which helps to
// eliminate "x=y" assignments that are the last use of "y".
var assignedRegs = {};
var freeRegsByType = copy(allRegsByType);
- // Begin with all live vars assigned per the entry-point.
- for (var name in liveness[0]) {
+ // Begin with all live vars assigned per the exit junction.
+ for (var name in jExit.live) {
var reg = junctionVariables[name].reg;
- assert(reg !== null, 'input variable doesnt have a register');
+ assert(reg !== null, 'output variable doesnt have a register');
assignedRegs[name] = reg;
delete freeRegsByType[localVars[name]][reg];
}
@@ -3023,66 +3009,71 @@ function registerizeHarder(ast) {
freeRegsByType[j] = keys(freeRegsByType[j]);
}
// Scan through the nodes in sequence, modifying each node in-place
- // and freeing registers according to the calculated liveness info.
- for (var j = 0; j < block.nodes.length; j++) {
+ // and grabbing/freeing registers as needed.
+ var maybeRemoveNodes = [];
+ for (var j = block.nodes.length - 1; j >= 0; j--) {
var node = block.nodes[j];
var name = node[0] === 'assign' ? node[2][1] : node[1];
var allRegs = allRegsByType[localVars[name]];
var freeRegs = freeRegsByType[localVars[name]];
var reg = assignedRegs[name];
if (node[0] === 'name') {
- // A use. It should already be in a register.
- assert(liveness[j][name], 'use node, but name was not alive?')
- assert(reg, 'live variable did not have a reg?')
- node[1] = allRegs[reg];
- } else {
- // A kill. This should assign it a new register.
- assert(!liveness[j][name], 'kill node, but name was alive?')
- assert(!reg, 'non-live variable still had a reg?')
- if (name in jExit.live && j === block.lastKillLoc[name]) {
- // Assignment to an output variable, must use pre-assigned reg.
- reg = junctionVariables[name].reg;
- assignedRegs[name] = reg;
- for (var k = freeRegs.length - 1; k >= 0; k--) {
- if (freeRegs[k] === reg) {
+ // A use. Grab a register if it doesn't have one.
+ if (!reg) {
+ if (name in inputVars && j <= block.firstDeadLoc[name]) {
+ // Assignment to an input variable, must use pre-assigned reg.
+ reg = junctionVariables[name].reg;
+ assignedRegs[name] = reg;
+ for (var k = freeRegs.length - 1; k >= 0; k--) {
+ if (freeRegs[k] === reg) {
+ freeRegs.splice(k, 1);
+ break;
+ }
+ }
+ } else {
+ // Try to use one of the existing free registers.
+ // It must not conflict with an input register.
+ for (var k = freeRegs.length - 1; k >= 0; k--) {
+ reg = freeRegs[k];
+ // Check for conflict with input registers.
+ if (block.firstKillLoc[name] <= inputDeadLoc[reg]) {
+ if (name !== inputVarsByReg[reg]) {
+ continue;
+ }
+ }
+ // Found one!
+ assignedRegs[name] = reg;
freeRegs.splice(k, 1);
break;
}
- }
- } else {
- // Try to use one of the existing free registers.
- // It must not conflict with an output register.
- for (var k = freeRegs.length - 1; k >= 0; k--) {
- reg = freeRegs[k];
- // Check for conflict with output registers.
- if (block.lastUseLoc[name] > outputAssignLoc[reg]) {
- if (name !== outputVars[reg]) {
- continue;
- }
+ // If we didn't find a suitable register, create a new one.
+ if (!assignedRegs[name]) {
+ reg = createReg(name);
+ assignedRegs[name] = reg;
}
- // Found one!
- assignedRegs[name] = reg;
- freeRegs.splice(k, 1);
- break;
- }
- // If we didn't find a suitable register, create a new one.
- if (!assignedRegs[name]) {
- reg = createReg(name);
- assignedRegs[name] = reg;
}
}
- // Modify assignment to use the new name.
- // If we happen to create a "x=x" type do-nothing assignment,
- // we can safely morph it into a no-op.
+ node[1] = allRegs[reg];
+ } else {
+ // A kill. This frees the assigned register.
+ assert(reg, 'live variable doesnt have a reg?')
node[2][1] = allRegs[reg];
- if (node[3][0] === 'name' && node[3][1] === node[2][1]) {
- morphNode(node, ['block', []]);
+ freeRegs.push(reg);
+ delete assignedRegs[name];
+ if (node[3][0] === 'name' && node[3][1] in localVars) {
+ maybeRemoveNodes.push([j, node]);
}
}
- // Free the reg if it's not live in the next step.
- if (!liveness[j+1][name]) {
- delete assignedRegs[name];
- freeRegs.push(reg);
+ }
+ // If we managed to create an "x=x" assignments, remove them.
+ for (var j = 0; j < maybeRemoveNodes.length; j++) {
+ var node = maybeRemoveNodes[j][1];
+ if (node[2][1] === node[3][1]) {
+ if (block.isexpr[maybeRemoveNodes[j][0]]) {
+ morphNode(node, node[2]);
+ } else {
+ morphNode(node, ['block', []]);
+ }
}
}
}
diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
index e030b707..9ba6643b 100644
--- a/tools/js_optimizer.py
+++ b/tools/js_optimizer.py
@@ -11,9 +11,9 @@ def path_from_root(*pathelems):
JS_OPTIMIZER = path_from_root('tools', 'js-optimizer.js')
-NUM_CHUNKS_PER_CORE = 1.5
-MIN_CHUNK_SIZE = int(os.environ.get('EMCC_JSOPT_MIN_CHUNK_SIZE') or 1024*1024) # configuring this is just for debugging purposes
-MAX_CHUNK_SIZE = 20*1024*1024
+NUM_CHUNKS_PER_CORE = 3
+MIN_CHUNK_SIZE = int(os.environ.get('EMCC_JSOPT_MIN_CHUNK_SIZE') or 512*1024) # configuring this is just for debugging purposes
+MAX_CHUNK_SIZE = 5*1024*1024
WINDOWS = sys.platform.startswith('win')
diff --git a/tools/shared.py b/tools/shared.py
index e28a66c3..f88eace0 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -345,7 +345,7 @@ def find_temp_directory():
# we re-check sanity when the settings are changed)
# We also re-check sanity and clear the cache when the version changes
-EMSCRIPTEN_VERSION = '1.9.3'
+EMSCRIPTEN_VERSION = '1.9.5'
def generate_sanity():
return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT + '|' + get_clang_version()
diff --git a/tools/test-js-optimizer-asm-regs-harder-output.js b/tools/test-js-optimizer-asm-regs-harder-output.js
new file mode 100644
index 00000000..448fb67a
--- /dev/null
+++ b/tools/test-js-optimizer-asm-regs-harder-output.js
@@ -0,0 +1,129 @@
+function asm(d1, i2) {
+ d1 = +d1;
+ i2 = i2 | 0;
+ i2 = d1 + d1 | 0;
+ d1 = d(Math_max(10, Math_min(5, f())));
+ i2 = i2 + 2 | 0;
+ print(i2);
+ d1 = d1 * 5;
+ return d1;
+}
+function _doit(i3, i2, i1) {
+ i3 = i3 | 0;
+ i2 = i2 | 0;
+ i1 = i1 | 0;
+ i3 = STACKTOP;
+ _printf(__str | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[(tempInt & 16777215) >> 2] = i2, HEAP32[(tempInt + 4 & 16777215) >> 2] = i1, tempInt));
+ STACKTOP = i3;
+ return 0 | 0;
+}
+function stackRestore(i1) {
+ i1 = i1 | 0;
+ STACKTOP = i1;
+}
+function switchey(d1, i2) {
+ d1 = +d1;
+ i2 = i2 | 0;
+ switch (d1 | 0) {
+ case 0:
+ i2 = d1 + d1 | 0;
+ d1 = d(Math_max(10, Math_min(5, f())));
+ i2 = i2 + 2 | 0;
+ print(i2);
+ d1 = d1 * 5;
+ return d1;
+ case 1:
+ return 20;
+ }
+}
+function switchey2() {
+ var i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, d6 = +0, d7 = +0;
+ i2 = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ i3 = 1;
+ while (1) switch (i3 | 0) {
+ case 1:
+ i1 = i2 | 0;
+ __ZN6RandomC1Ev(i1);
+ i4 = 0;
+ i5 = 0;
+ i3 = 2;
+ break;
+ case 2:
+ d7 = +__ZN6Random3getEf(8, +1);
+ d6 = +__ZN6Random3getEf(i1, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d7, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d6, tempInt) | 0);
+ i5 = (d7 != d6 & 1) + i5 | 0;
+ i4 = i4 + 1 | 0;
+ if ((i4 | 0) < 100) {
+ i3 = 2;
+ break;
+ } else {
+ i3 = 3;
+ break;
+ }
+ case 3:
+ _printf(16, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[CHECK_ALIGN_4(tempInt | 0) >> 2] = i5, tempInt) | 0);
+ STACKTOP = i2;
+ return 0;
+ }
+ return 0;
+}
+function iffey() {
+ var i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, d6 = +0, d7 = +0;
+ i2 = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ i4 = 1;
+ while (1) {
+ if (i4 | 0) {
+ i1 = i2 | 0;
+ __ZN6RandomC1Ev(i1);
+ i3 = 0;
+ i5 = 0;
+ i4 = 2;
+ } else {
+ d7 = +__ZN6Random3getEf(8, +1);
+ d6 = +__ZN6Random3getEf(i1, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d7, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d6, tempInt) | 0);
+ i5 = (d7 != d6 & 1) + i5 | 0;
+ i3 = i3 + 1 | 0;
+ if ((i3 | 0) < 100) {
+ i4 = 2;
+ } else {
+ return 10;
+ }
+ }
+ }
+ return 0;
+}
+function labelledJump(i3) {
+ i3 = i3 | 0;
+ var i1 = 0, i2 = 0;
+ i2 = 2;
+ if (i3) {
+ i2 = 17;
+ i1 = 1;
+ }
+ if (i1 == 1) {
+ i3 = i2 + 1;
+ } else {
+ i3 = i2 + 1;
+ }
+ return i3;
+}
+function linkedVars() {
+ var i1 = 0, i2 = 0;
+ while (1) {
+ i2 = 9;
+ i1 = 5;
+ while (i2 > 0 || i1 > 0) {
+ if (i2 < i1) {
+ i2 = i2 - 1;
+ } else {
+ i1 = i1 - 1;
+ }
+ }
+ }
+ return i2 + i1;
+}
+
diff --git a/tools/test-js-optimizer-asm-regs-harder.js b/tools/test-js-optimizer-asm-regs-harder.js
new file mode 100644
index 00000000..cf44c1dd
--- /dev/null
+++ b/tools/test-js-optimizer-asm-regs-harder.js
@@ -0,0 +1,150 @@
+function asm(x, y) {
+ x = +x;
+ y = y | 0;
+ var int1 = 0, int2 = 0; // do not mix the types!
+ var double1 = +0, double2 = +0;
+ int1 = (x+x)|0;
+ double1 = d(Math_max(10, Math_min(5, f())));
+ int2 = (int1+2)|0;
+ print(int2);
+ double2 = double1*5;
+ return double2;
+}
+function _doit($x, $y$0, $y$1) {
+ $x = $x | 0;
+ $y$0 = $y$0 | 0;
+ $y$1 = $y$1 | 0;
+ var __stackBase__ = 0;
+ __stackBase__ = STACKTOP;
+ _printf(__str | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[(tempInt & 16777215) >> 2] = $y$0, HEAP32[(tempInt + 4 & 16777215) >> 2] = $y$1, tempInt));
+ STACKTOP = __stackBase__;
+ return 0 | 0;
+}
+function stackRestore(top) {
+ top = top|0;
+ STACKTOP = top;
+}
+function switchey(x, y) {
+ x = +x;
+ y = y | 0;
+ var int1 = 0, int2 = 0; // do not mix the types!
+ var double1 = +0, double2 = +0;
+ switch(x|0) {
+ case 0:
+ int1 = (x+x)|0;
+ double1 = d(Math_max(10, Math_min(5, f())));
+ int2 = (int1+2)|0;
+ print(int2);
+ double2 = double1*5;
+ return double2;
+ case 1:
+ return 20;
+ }
+}
+function switchey2() {
+ var $rng2 = 0, $count_06 = 0, $i_05 = 0, $2 = +0, $3 = +0, $count_1 = 0, $9 = 0, label = 0, __stackBase__ = 0;
+ __stackBase__ = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ label = 1;
+ while (1) switch (label | 0) {
+ case 1:
+ $rng2 = __stackBase__ | 0;
+ __ZN6RandomC1Ev($rng2);
+ $i_05 = 0;
+ $count_06 = 0;
+ label = 2;
+ break;
+ case 2:
+ $2 = +__ZN6Random3getEf(8, +1);
+ $3 = +__ZN6Random3getEf($rng2, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = $2, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = $3, tempInt) | 0);
+ $count_1 = ($2 != $3 & 1) + $count_06 | 0;
+ $9 = $i_05 + 1 | 0;
+ if (($9 | 0) < 100) {
+ $i_05 = $9;
+ $count_06 = $count_1;
+ label = 2;
+ break;
+ } else {
+ label = 3;
+ break;
+ }
+ case 3:
+ _printf(16, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[CHECK_ALIGN_4(tempInt | 0) >> 2] = $count_1, tempInt) | 0);
+ STACKTOP = __stackBase__;
+ return 0;
+ }
+ return 0;
+}
+function iffey() {
+ var $rng2 = 0, $count_06 = 0, $i_05 = 0, $2 = +0, $3 = +0, $count_1 = 0, $9 = 0, label = 0, __stackBase__ = 0;
+ __stackBase__ = STACKTOP;
+ STACKTOP = STACKTOP + 8 | 0;
+ label = 1;
+ while (1) {
+ if (label | 0) {
+ $rng2 = __stackBase__ | 0;
+ __ZN6RandomC1Ev($rng2);
+ $i_05 = 0;
+ $count_06 = 0;
+ label = 2;
+ } else {
+ $2 = +__ZN6Random3getEf(8, +1);
+ $3 = +__ZN6Random3getEf($rng2, +1);
+ _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = $2, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = $3, tempInt) | 0);
+ $count_1 = ($2 != $3 & 1) + $count_06 | 0;
+ $9 = $i_05 + 1 | 0;
+ if (($9 | 0) < 100) {
+ $i_05 = $9;
+ $count_06 = $count_1;
+ label = 2;
+ } else {
+ label = 3;
+ return 10;
+ }
+ }
+ }
+ return 0;
+}
+function labelledJump(x) {
+ x = x | 0;
+ var label = 0
+ // y and z don't conflict, but only if you know about labelled jumps.
+ var y = 0, z = 0;
+ y = 2;
+ if (x) {
+ z = 17;
+ label = 1;
+ }
+ if (label == 1) {
+ x = z + 1;
+ } else {
+ x = y + 1;
+ }
+ return x;
+}
+function linkedVars() {
+ var outer1 = 0, outer2 = 0;
+ var inner1_0 = 0, inner1_1 = 0, inner2_0 = 0, inner2_1 = 0;
+ while (1) {
+ outer1 = 9;
+ outer2 = 5;
+ while (outer1 > 0 || outer2 > 0) {
+ // All these copy assignment should be eliminated by var sharing.
+ inner1_0 = outer1;
+ inner2_0 = outer2;
+ if (inner1_0 < inner2_0) {
+ inner1_1 = inner1_0 - 1;
+ inner2_1 = inner2_0;
+ } else {
+ inner1_1 = inner1_0;
+ inner2_1 = inner2_0 - 1;
+ }
+ outer1 = inner1_1;
+ outer2 = inner2_1;
+ }
+ }
+ return outer1 + outer2;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "stackRestore", "switchey", "switchey2", "iffey", "labelledJump", "linkedVars']
+