aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/library.js10
-rw-r--r--src/library_browser.js113
-rw-r--r--src/library_fs.js14
-rw-r--r--src/library_glfw.js8
-rw-r--r--src/library_sdl.js227
-rw-r--r--src/modules.js1
-rw-r--r--src/parseTools.js4
-rw-r--r--src/preamble.js8
-rw-r--r--src/relooper/Relooper.cpp114
-rw-r--r--src/relooper/Relooper.h14
-rw-r--r--src/settings.js4
-rw-r--r--src/shell.html7
-rw-r--r--src/shell_minimal.html7
-rw-r--r--src/struct_info.json23
14 files changed, 385 insertions, 169 deletions
diff --git a/src/library.js b/src/library.js
index 1d5a9140..c2830397 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3908,12 +3908,18 @@ LibraryManager.library = {
{{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}};
},
+ llvm_bswap_i16__asm: true,
+ llvm_bswap_i16__sig: 'ii',
llvm_bswap_i16: function(x) {
- return ((x&0xff)<<8) | ((x>>8)&0xff);
+ x = x|0;
+ return (((x&0xff)<<8) | ((x>>8)&0xff))|0;
},
+ llvm_bswap_i32__asm: true,
+ llvm_bswap_i32__sig: 'ii',
llvm_bswap_i32: function(x) {
- return ((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24);
+ x = x|0;
+ return (((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24))|0;
},
llvm_bswap_i64__deps: ['llvm_bswap_i32'],
diff --git a/src/library_browser.js b/src/library_browser.js
index 0808b9f0..4cd8b392 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -196,41 +196,42 @@ mergeInto(LibraryManager.library, {
// Canvas event setup
var canvas = Module['canvas'];
-
- // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module
- // Module['forcedAspectRatio'] = 4 / 3;
-
- canvas.requestPointerLock = canvas['requestPointerLock'] ||
- canvas['mozRequestPointerLock'] ||
- canvas['webkitRequestPointerLock'] ||
- canvas['msRequestPointerLock'] ||
- function(){};
- canvas.exitPointerLock = document['exitPointerLock'] ||
- document['mozExitPointerLock'] ||
- document['webkitExitPointerLock'] ||
- document['msExitPointerLock'] ||
- function(){}; // no-op if function does not exist
- canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
-
- function pointerLockChange() {
- Browser.pointerLock = document['pointerLockElement'] === canvas ||
- document['mozPointerLockElement'] === canvas ||
- document['webkitPointerLockElement'] === canvas ||
- document['msPointerLockElement'] === canvas;
- }
+ if (canvas) {
+ // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module
+ // Module['forcedAspectRatio'] = 4 / 3;
+
+ canvas.requestPointerLock = canvas['requestPointerLock'] ||
+ canvas['mozRequestPointerLock'] ||
+ canvas['webkitRequestPointerLock'] ||
+ canvas['msRequestPointerLock'] ||
+ function(){};
+ canvas.exitPointerLock = document['exitPointerLock'] ||
+ document['mozExitPointerLock'] ||
+ document['webkitExitPointerLock'] ||
+ document['msExitPointerLock'] ||
+ function(){}; // no-op if function does not exist
+ canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
+
+ function pointerLockChange() {
+ Browser.pointerLock = document['pointerLockElement'] === canvas ||
+ document['mozPointerLockElement'] === canvas ||
+ document['webkitPointerLockElement'] === canvas ||
+ document['msPointerLockElement'] === canvas;
+ }
- document.addEventListener('pointerlockchange', pointerLockChange, false);
- document.addEventListener('mozpointerlockchange', pointerLockChange, false);
- document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
- document.addEventListener('mspointerlockchange', pointerLockChange, false);
+ document.addEventListener('pointerlockchange', pointerLockChange, false);
+ document.addEventListener('mozpointerlockchange', pointerLockChange, false);
+ document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+ document.addEventListener('mspointerlockchange', pointerLockChange, false);
- if (Module['elementPointerLock']) {
- canvas.addEventListener("click", function(ev) {
- if (!Browser.pointerLock && canvas.requestPointerLock) {
- canvas.requestPointerLock();
- ev.preventDefault();
- }
- }, false);
+ if (Module['elementPointerLock']) {
+ canvas.addEventListener("click", function(ev) {
+ if (!Browser.pointerLock && canvas.requestPointerLock) {
+ canvas.requestPointerLock();
+ ev.preventDefault();
+ }
+ }, false);
+ }
}
},
@@ -482,6 +483,8 @@ mergeInto(LibraryManager.library, {
mouseY: 0,
mouseMovementX: 0,
mouseMovementY: 0,
+ touches: {},
+ lastTouches: {},
calculateMouseEvent: function(event) { // event should be mousemove, mousedown or mouseup
if (Browser.pointerLock) {
@@ -510,8 +513,9 @@ mergeInto(LibraryManager.library, {
// Otherwise, calculate the movement based on the changes
// in the coordinates.
var rect = Module["canvas"].getBoundingClientRect();
- var x, y;
-
+ var cw = Module["canvas"].width;
+ var ch = Module["canvas"].height;
+
// Neither .scrollX or .pageXOffset are defined in a spec, but
// we prefer .scrollX because it is currently in a spec draft.
// (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/)
@@ -522,26 +526,37 @@ mergeInto(LibraryManager.library, {
// and we have no viable fallback.
assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.');
#endif
- if (event.type == 'touchstart' ||
- event.type == 'touchend' ||
- event.type == 'touchmove') {
- var t = event.touches.item(0);
- if (t) {
- x = t.pageX - (scrollX + rect.left);
- y = t.pageY - (scrollY + rect.top);
- } else {
- return;
+
+ if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') {
+ var touch = event.touch;
+ if (touch === undefined) {
+ return; // the "touch" property is only defined in SDL
+
}
- } else {
- x = event.pageX - (scrollX + rect.left);
- y = event.pageY - (scrollY + rect.top);
+ var adjustedX = touch.pageX - (scrollX + rect.left);
+ var adjustedY = touch.pageY - (scrollY + rect.top);
+
+ adjustedX = adjustedX * (cw / rect.width);
+ adjustedY = adjustedY * (ch / rect.height);
+
+ var coords = { x: adjustedX, y: adjustedY };
+
+ if (event.type === 'touchstart') {
+ Browser.lastTouches[touch.identifier] = coords;
+ Browser.touches[touch.identifier] = coords;
+ } else if (event.type === 'touchend' || event.type === 'touchmove') {
+ Browser.lastTouches[touch.identifier] = Browser.touches[touch.identifier];
+ Browser.touches[touch.identifier] = { x: adjustedX, y: adjustedY };
+ }
+ return;
}
+ var x = event.pageX - (scrollX + rect.left);
+ var y = event.pageY - (scrollY + rect.top);
+
// the canvas might be CSS-scaled compared to its backbuffer;
// SDL-using content will want mouse coordinates in terms
// of backbuffer units.
- var cw = Module["canvas"].width;
- var ch = Module["canvas"].height;
x = x * (cw / rect.width);
y = y * (ch / rect.height);
diff --git a/src/library_fs.js b/src/library_fs.js
index 3d0036ee..d53210f9 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -394,16 +394,12 @@ mergeInto(LibraryManager.library, {
}
});
}
- if (stream.__proto__) {
- // reuse the object
- stream.__proto__ = FS.FSStream.prototype;
- } else {
- var newStream = new FS.FSStream();
- for (var p in stream) {
- newStream[p] = stream[p];
- }
- stream = newStream;
+ // clone it, so we can return an instance of FSStream
+ var newStream = new FS.FSStream();
+ for (var p in stream) {
+ newStream[p] = stream[p];
}
+ stream = newStream;
var fd = FS.nextfd(fd_start, fd_end);
stream.fd = fd;
FS.streams[fd] = stream;
diff --git a/src/library_glfw.js b/src/library_glfw.js
index f72aeb24..6d539326 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -130,9 +130,11 @@ var LibraryGLFW = {
onKeyChanged: function(event, status) {
var key = GLFW.DOMToGLFWKeyCode(event.keyCode);
- if (key && GLFW.keyFunc) {
+ if (key) {
GLFW.keys[key] = status;
- Runtime.dynCall('vii', GLFW.keyFunc, [key, status]);
+ if (GLFW.keyFunc) {
+ Runtime.dynCall('vii', GLFW.keyFunc, [key, status]);
+ }
}
},
@@ -209,7 +211,7 @@ var LibraryGLFW = {
},
onMouseWheel: function(event) {
- GLFW.wheelPos += Browser.getMouseWheelDelta(event);
+ GLFW.wheelPos -= Browser.getMouseWheelDelta(event);
if (GLFW.mouseWheelFunc && event.target == Module["canvas"]) {
Runtime.dynCall('vi', GLFW.mouseWheelFunc, [GLFW.wheelPos]);
diff --git a/src/library_sdl.js b/src/library_sdl.js
index d90484ad..6e235e90 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -10,7 +10,7 @@
// or otherwise).
var LibrarySDL = {
- $SDL__deps: ['$FS', '$PATH', '$Browser'],
+ $SDL__deps: ['$FS', '$PATH', '$Browser', 'SDL_GetTicks'],
$SDL: {
defaults: {
width: 320,
@@ -82,6 +82,8 @@ var LibrarySDL = {
DOMEventToSDLEvent: {},
+ TOUCH_DEFAULT_ID: 0, // Our default deviceID for touch events (we get nothing from the browser)
+
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
// For keys that don't have unicode value, we map DOM codes with the corresponding scan codes + 1024 (using "| 1 << 10")
16: 225 | 1<<10, // shift
@@ -417,50 +419,104 @@ var LibrarySDL = {
}
},
- touchX: 0, touchY: 0,
+ // the browser sends out touchstart events with the whole group of touches
+ // even if we received a previous touchstart for a specific touch identifier.
+ // You can test this by pressing one finger to the screen, then another. You'll
+ // receive two touchstart events, the first with a touches count of 1 the second
+ // with a touches count of two.
+ // SDL sends out a new touchstart event for only each newly started touch so to
+ // emulate this, we keep track of previously started touches.
+ downFingers: {},
savedKeydown: null,
receiveEvent: function(event) {
switch(event.type) {
- case 'touchstart':
+ case 'touchstart': case 'touchmove': {
event.preventDefault();
- var touch = event.touches[0];
- touchX = touch.pageX;
- touchY = touch.pageY;
- var event = {
- type: 'mousedown',
+
+ var touches = [];
+
+ // Clear out any touchstart events that we've already processed
+ if (event.type === 'touchstart') {
+ for (var i = 0; i < event.touches.length; i++) {
+ var touch = event.touches[i];
+ if (SDL.downFingers[touch.identifier] != true) {
+ SDL.downFingers[touch.identifier] = true;
+ touches.push(touch);
+ }
+ }
+ } else {
+ touches = event.touches;
+ }
+
+ var firstTouch = touches[0];
+ if (event.type == 'touchstart') {
+ SDL.DOMButtons[0] = 1;
+ }
+ var mouseEventType;
+ switch(event.type) {
+ case 'touchstart': mouseEventType = 'mousedown'; break;
+ case 'touchmove': mouseEventType = 'mousemove'; break;
+ }
+ var mouseEvent = {
+ type: mouseEventType,
button: 0,
- pageX: touchX,
- pageY: touchY
+ pageX: firstTouch.clientX,
+ pageY: firstTouch.clientY
};
- SDL.DOMButtons[0] = 1;
- SDL.events.push(event);
- break;
- case 'touchmove':
- event.preventDefault();
- var touch = event.touches[0];
- touchX = touch.pageX;
- touchY = touch.pageY;
- event = {
- type: 'mousemove',
- button: 0,
- pageX: touchX,
- pageY: touchY
+ SDL.events.push(mouseEvent);
+
+ for (var i = 0; i < touches.length; i++) {
+ var touch = touches[i];
+ SDL.events.push({
+ type: event.type,
+ touch: touch
+ });
};
- SDL.events.push(event);
break;
- case 'touchend':
+ }
+ case 'touchend': {
event.preventDefault();
- event = {
+
+ // Remove the entry in the SDL.downFingers hash
+ // because the finger is no longer down.
+ for(var i = 0; i < event.changedTouches.length; i++) {
+ var touch = event.changedTouches[i];
+ if (SDL.downFingers[touch.identifier] === true) {
+ delete SDL.downFingers[touch.identifier];
+ }
+ }
+
+ var mouseEvent = {
type: 'mouseup',
button: 0,
- pageX: touchX,
- pageY: touchY
+ pageX: event.changedTouches[0].clientX,
+ pageY: event.changedTouches[0].clientY
};
SDL.DOMButtons[0] = 0;
- SDL.events.push(event);
+ SDL.events.push(mouseEvent);
+
+ for (var i = 0; i < event.changedTouches.length; i++) {
+ var touch = event.changedTouches[i];
+ SDL.events.push({
+ type: 'touchend',
+ touch: touch
+ });
+ };
break;
+ }
case 'mousemove':
+ if (SDL.DOMButtons[0] === 1) {
+ SDL.events.push({
+ type: 'touchmove',
+ touch: {
+ identifier: 0,
+ deviceID: {{{ cDefine('SDL_TOUCH_MOUSEID') }}},
+ pageX: event.pageX,
+ pageY: event.pageY
+ }
+ });
+ }
if (Browser.pointerLock) {
// workaround for firefox bug 750111
if ('mozMovementX' in event) {
@@ -502,6 +558,15 @@ var LibrarySDL = {
};
} else if (event.type == 'mousedown') {
SDL.DOMButtons[event.button] = 1;
+ SDL.events.push({
+ type: 'touchstart',
+ touch: {
+ identifier: 0,
+ deviceID: {{{ cDefine('SDL_TOUCH_MOUSEID') }}},
+ pageX: event.pageX,
+ pageY: event.pageY
+ }
+ });
} else if (event.type == 'mouseup') {
// ignore extra ups, can happen if we leave the canvas while pressing down, then return,
// since we add a mouseup in that case
@@ -509,6 +574,15 @@ var LibrarySDL = {
return;
}
+ SDL.events.push({
+ type: 'touchend',
+ touch: {
+ identifier: 0,
+ deviceID: {{{ cDefine('SDL_TOUCH_MOUSEID') }}},
+ pageX: event.pageX,
+ pageY: event.pageY
+ }
+ });
SDL.DOMButtons[event.button] = 0;
}
@@ -600,6 +674,10 @@ var LibrarySDL = {
event.handled = true;
switch (event.type) {
+ case 'touchstart': case 'touchend': case 'touchmove': {
+ Browser.calculateMouseEvent(event);
+ break;
+ }
case 'keydown': case 'keyup': {
var down = event.type === 'keydown';
var code = event.keyCode;
@@ -608,8 +686,11 @@ var LibrarySDL = {
} else {
code = SDL.keyCodes[event.keyCode] || event.keyCode;
}
+
+ var scan = code & ~(1 << 10);
+ scan = SDL.scanCodes[scan] || scan;
- {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}};
+ {{{ makeSetValue('SDL.keyboardState', 'scan', 'down', 'i8') }}};
// TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED
SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL
({{{ makeGetValue('SDL.keyboardState', '1249', 'i8') }}} ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT
@@ -690,13 +771,19 @@ var LibrarySDL = {
if (event.type != 'mousemove') {
var down = event.type === 'mousedown';
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.timestamp, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.windowID, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.which, '0', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.button, 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.state, 'down ? 1 : 0', 'i8') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.x, 'Browser.mouseX', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseButtonEvent.y, 'Browser.mouseY', 'i32') }}};
} else {
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
- {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.state, 'SDL.buttonState', 'i8') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.timestamp, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.windowID, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.which, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.state, 'SDL.buttonState', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.x, 'Browser.mouseX', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.y, 'Browser.mouseY', 'i32') }}};
{{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseMotionEvent.xrel, 'Browser.mouseMovementX', 'i32') }}};
@@ -704,6 +791,33 @@ var LibrarySDL = {
}
break;
}
+ case 'touchstart': case 'touchend': case 'touchmove': {
+ var touch = event.touch;
+ var w = Module['canvas'].width;
+ var h = Module['canvas'].height;
+ var x = Browser.touches[touch.identifier].x / w;
+ var y = Browser.touches[touch.identifier].y / h;
+ var lx = Browser.lastTouches[touch.identifier].x / w;
+ var ly = Browser.lastTouches[touch.identifier].y / h;
+ var dx = x - lx;
+ var dy = y - ly;
+ if (touch['deviceID'] === undefined) touch.deviceID = SDL.TOUCH_DEFAULT_ID;
+ if (dx === 0 && dy === 0 && event.type === 'touchmove') return; // don't send these if nothing happened
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.timestamp, '_SDL_GetTicks()', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.touchId, 'touch.deviceID', 'i64') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.fingerId, 'touch.identifier', 'i64') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.x, 'x', 'float') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.y, 'y', 'float') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.dx, 'dx', 'float') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.dy, 'dy', 'float') }}};
+ if (touch.force !== undefined) {
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.pressure, 'touch.force', 'float') }}};
+ } else { // No pressure data, send a digital 0/1 pressure.
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_TouchFingerEvent.pressure, 'event.type == "touchend" ? 0 : 1', 'float') }}};
+ }
+ break;
+ }
case 'unload': {
{{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
break;
@@ -948,14 +1062,17 @@ var LibrarySDL = {
SDL.keyboardState = _malloc(0x10000); // Our SDL needs 512, but 64K is safe for older SDLs
_memset(SDL.keyboardState, 0, 0x10000);
// Initialize this structure carefully for closure
- SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
- SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
- SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */;
- SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
- SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
- SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
- SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
- SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
+ SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
+ SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
+ SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */;
+ SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
+ SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
+ SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
+ SDL.DOMEventToSDLEvent['touchstart'] = 0x700 /* SDL_FINGERDOWN */;
+ SDL.DOMEventToSDLEvent['touchend'] = 0x701 /* SDL_FINGERUP */;
+ SDL.DOMEventToSDLEvent['touchmove'] = 0x702 /* SDL_FINGERMOTION */;
+ SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
+ SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
// These are not technically DOM events; the HTML gamepad API is poll-based.
// However, we define them here, as the rest of the SDL code assumes that
// all SDL events originate as DOM events.
@@ -1025,7 +1142,7 @@ var LibrarySDL = {
},
SDL_SetVideoMode: function(width, height, depth, flags) {
- ['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
+ ['touchstart', 'touchend', 'touchmove', 'mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
@@ -1476,20 +1593,28 @@ var LibrarySDL = {
return 0;
},
- SDL_PeepEvents: function(events, numEvents, action, from, to) {
+ SDL_PeepEvents: function(events, requestedEventCount, action, from, to) {
switch(action) {
case 2: { // SDL_GETEVENT
- assert(numEvents == 1);
- var got = 0;
- while (SDL.events.length > 0 && numEvents > 0) {
- var type = SDL.DOMEventToSDLEvent[SDL.events[0].type];
- if (type < from || type > to) break;
- SDL.makeCEvent(SDL.events.shift(), events);
- got++;
- numEvents--;
- // events += sizeof(..)
+ // We only handle 1 event right now
+ assert(requestedEventCount == 1);
+
+ var index = 0;
+ var retrievedEventCount = 0;
+ // this should look through the entire queue until it has filled up the events
+ // array
+ while (index < SDL.events.length && retrievedEventCount < requestedEventCount) {
+ var event = SDL.events[index];
+ var type = SDL.DOMEventToSDLEvent[event.type];
+ if (from <= type && type <= to) {
+ SDL.makeCEvent(event, events);
+ SDL.events.splice(index, 1);
+ retrievedEventCount++;
+ } else {
+ index++;
+ }
}
- return got;
+ return retrievedEventCount;
}
default: throw 'SDL_PeepEvents does not yet support that action: ' + action;
}
diff --git a/src/modules.js b/src/modules.js
index 2d2a75d0..fd5c23cd 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -21,6 +21,7 @@ var LLVM = {
CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui', 'fpext', 'fptrunc'),
INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
MATHOP_IGNORABLES: set('exact', 'nnan', 'ninf', 'nsz', 'arcp', 'fast'),
+ PARAM_IGNORABLES: set('nocapture', 'readonly', 'readnone'),
};
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
diff --git a/src/parseTools.js b/src/parseTools.js
index 4fb76196..4396abd9 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -467,7 +467,7 @@ function parseParamTokens(params) {
// handle 'byval' and 'byval align X'. We store the alignment in 'byVal'
byVal = QUANTUM_SIZE;
segment.splice(1, 1);
- if (segment[1] && (segment[1].text === 'nocapture' || segment[1].text === 'readonly')) {
+ if (segment[1] && (segment[1].text in LLVM.PARAM_IGNORABLES)) {
segment.splice(1, 1);
}
if (segment[1] && segment[1].text === 'align') {
@@ -476,7 +476,7 @@ function parseParamTokens(params) {
segment.splice(1, 2);
}
}
- if (segment[1] && (segment[1].text === 'nocapture' || segment[1].text === 'readonly')) {
+ if (segment[1] && (segment[1].text in LLVM.PARAM_IGNORABLES)) {
segment.splice(1, 1);
}
if (segment.length == 1) {
diff --git a/src/preamble.js b/src/preamble.js
index 89ab5026..5dd8ada1 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -207,6 +207,14 @@ function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) {
return ret;
}
+function SAFE_FT_MASK(value, mask) {
+ var ret = value & mask;
+ if (ret !== value) {
+ abort('Function table mask error: function pointer is ' + value + ' which is masked by ' + mask + ', the likely cause of this is that the function pointer is being called by the wrong type.');
+ }
+ return ret;
+}
+
#endif
#endif
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index 780a6d59..ce9232d9 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -243,7 +243,7 @@ void Block::Render(bool InLoop) {
Details = ProcessedBranchesOut[DefaultTarget];
}
bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel;
- bool HasFusedContent = Fused && contains(Fused->InnerMap, Target);
+ bool HasFusedContent = Fused && contains(Fused->InnerMap, Target->Id);
bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code;
if (iter != ProcessedBranchesOut.end()) {
// If there is nothing to show in this branch, omit the condition
@@ -286,7 +286,7 @@ void Block::Render(bool InLoop) {
if (!First) Indenter::Indent();
Details->Render(Target, SetCurrLabel);
if (HasFusedContent) {
- Fused->InnerMap.find(Target)->second->Render(InLoop);
+ Fused->InnerMap.find(Target->Id)->second->Render(InLoop);
} else if (Details->Type == Branch::Nested) {
// Nest the parent content here, and remove it from showing up afterwards as Next
assert(Parent->Next);
@@ -312,18 +312,28 @@ void Block::Render(bool InLoop) {
// MultipleShape
void MultipleShape::RenderLoopPrefix() {
- if (NeedLoop) {
- if (Labeled) {
- PrintIndented("L%d: do {\n", Id);
+ if (Breaks) {
+ if (UseSwitch) {
+ if (Labeled) {
+ PrintIndented("L%d: ", Id);
+ }
} else {
- PrintIndented("do {\n");
+ if (Labeled) {
+ if (UseSwitch) {
+ PrintIndented("L%d: ", Id);
+ } else {
+ PrintIndented("L%d: do {\n", Id);
+ }
+ } else {
+ PrintIndented("do {\n");
+ }
+ Indenter::Indent();
}
- Indenter::Indent();
}
}
void MultipleShape::RenderLoopPostfix() {
- if (NeedLoop) {
+ if (Breaks && !UseSwitch) {
Indenter::Unindent();
PrintIndented("} while(0);\n");
}
@@ -332,32 +342,41 @@ void MultipleShape::RenderLoopPostfix() {
void MultipleShape::Render(bool InLoop) {
RenderLoopPrefix();
- // We know that blocks with the same Id were split from the same source, so their contents are identical and they are logically the same, so re-merge them here
- typedef std::map<int, Shape*> IdShapeMap;
- IdShapeMap IdMap;
- for (BlockShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
- int Id = iter->first->Id;
- IdShapeMap::iterator Test = IdMap.find(Id);
- if (Test != IdMap.end()) {
- assert(Shape::IsSimple(iter->second) && Shape::IsSimple(Test->second)); // we can only merge simple blocks, something horrible has gone wrong if we see anything else
- continue;
+ if (!UseSwitch) {
+ // emit an if-else chain
+ bool First = true;
+ for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
+ if (AsmJS) {
+ PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first);
+ } else {
+ PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first);
+ }
+ First = false;
+ Indenter::Indent();
+ iter->second->Render(InLoop);
+ Indenter::Unindent();
+ PrintIndented("}\n");
}
- IdMap[iter->first->Id] = iter->second;
- }
-
- bool First = true;
- for (IdShapeMap::iterator iter = IdMap.begin(); iter != IdMap.end(); iter++) {
+ } else {
+ // emit a switch
if (AsmJS) {
- PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first);
+ PrintIndented("switch (label|0) {\n");
} else {
- PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first);
+ PrintIndented("switch (label) {\n");
}
- First = false;
Indenter::Indent();
- iter->second->Render(InLoop);
+ for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
+ PrintIndented("case %d: {\n", iter->first);
+ Indenter::Indent();
+ iter->second->Render(InLoop);
+ PrintIndented("break;\n");
+ Indenter::Unindent();
+ PrintIndented("}\n");
+ }
Indenter::Unindent();
PrintIndented("}\n");
}
+
RenderLoopPostfix();
if (Next) Next->Render(InLoop);
}
@@ -547,7 +566,7 @@ void Relooper::Calculate(Block *Entry) {
PriorOut->Ancestor = Ancestor;
PriorOut->Type = Type;
if (MultipleShape *Multiple = Shape::IsMultiple(Ancestor)) {
- Multiple->NeedLoop++; // We are breaking out of this Multiple, so need a loop
+ Multiple->Breaks++; // We are breaking out of this Multiple, so need a loop
}
iter++; // carefully increment iter before erasing
Target->BranchesIn.erase(Prior);
@@ -853,7 +872,7 @@ void Relooper::Calculate(Block *Entry) {
iter = Next; // increment carefully because Solipsize can remove us
}
}
- Multiple->InnerMap[CurrEntry] = Process(CurrBlocks, CurrEntries, NULL);
+ Multiple->InnerMap[CurrEntry->Id] = Process(CurrBlocks, CurrEntries, NULL);
// If we are not fused, then our entries will actually be checked
if (!Fused) {
CurrEntry->IsCheckedMultipleEntry = true;
@@ -867,6 +886,11 @@ void Relooper::Calculate(Block *Entry) {
NextEntries.insert(Entry);
}
}
+ // The multiple has been created, we can decide how to implement it
+ if (Multiple->InnerMap.size() >= 10) {
+ Multiple->UseSwitch = true;
+ Multiple->Breaks++; // switch captures breaks
+ }
return Multiple;
}
@@ -1021,7 +1045,7 @@ void Relooper::Calculate(Block *Entry) {
PostOptimizer(Relooper *ParentInit) : Parent(ParentInit), Closure(NULL) {}
#define RECURSE_Multiple(shape, func) \
- for (BlockShapeMap::iterator iter = shape->InnerMap.begin(); iter != shape->InnerMap.end(); iter++) { \
+ for (IdShapeMap::iterator iter = shape->InnerMap.begin(); iter != shape->InnerMap.end(); iter++) { \
func(iter->second); \
}
#define RECURSE_Loop(shape, func) \
@@ -1042,7 +1066,7 @@ void Relooper::Calculate(Block *Entry) {
SHAPE_SWITCH(S, {
Out.insert(Simple->Inner);
}, {
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
FollowNaturalFlow(iter->second, Out);
}
FollowNaturalFlow(Multiple->Next, Out);
@@ -1061,7 +1085,7 @@ void Relooper::Calculate(Block *Entry) {
SHAPE_SWITCH(Root, {
}, {
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
FindNaturals(iter->seco