aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/experimental/sdl_key_forwarding.diff57
-rw-r--r--src/library.js45
-rw-r--r--src/library_browser.js31
-rw-r--r--src/library_sdl.js119
-rw-r--r--src/postamble.js26
-rw-r--r--src/settings.js12
6 files changed, 229 insertions, 61 deletions
diff --git a/src/experimental/sdl_key_forwarding.diff b/src/experimental/sdl_key_forwarding.diff
new file mode 100644
index 00000000..395dc110
--- /dev/null
+++ b/src/experimental/sdl_key_forwarding.diff
@@ -0,0 +1,57 @@
+diff --git a/src/library_sdl.js b/src/library_sdl.js
+index 8cb8db7..d46a089 100644
+--- a/src/library_sdl.js
++++ b/src/library_sdl.js
+@@ -43,6 +43,7 @@ var LibrarySDL = {
+ DOMButtons: [0, 0, 0],
+
+ DOMEventToSDLEvent: {},
++ forwardedDOMKeys: {},
+
+ keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
+ 46: 127, // SDLK_DEL == '\177'
+@@ -372,6 +373,11 @@ var LibrarySDL = {
+ if (!SDL.DOMButtons[event.button]) return false; // ignore extra ups, can happen if we leave the canvas while pressing down, then return,
+ // since we add a mouseup in that case
+ SDL.DOMButtons[event.button] = 0;
++ } else if (event.type == 'keydown' || event.type == 'keyup') {
++ // whitelist a few keycodes that we do want to let the browser handle
++ if (event.keyCode in SDL.forwardedDOMKeys) {
++ return true;
++ }
+ }
+
+ SDL.events.push(event);
+@@ -399,6 +405,10 @@ var LibrarySDL = {
+ // Force-run a main event loop, since otherwise this event will never be caught!
+ Browser.mainLoop.runner();
+ return true;
++ case 'keypress':
++ if (event.ctrlKey) {
++ return true; // forward control-X events, see SDL.forwardedDOMKeys
++ }
+ }
+ return false;
+ },
+@@ -569,13 +579,20 @@ var LibrarySDL = {
+ window.onunload = SDL.receiveEvent;
+ SDL.keyboardState = _malloc(0x10000);
+ _memset(SDL.keyboardState, 0, 0x10000);
+- // Initialize this structure carefully for closure
++ // Initialize these structures carefully for closure
+ SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
+ SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
+ 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.forwardedDOMKeys[17] = 1; // control - forward control-X to keep the page responsive
++ SDL.forwardedDOMKeys[173] = 1; // minus (shrink view)
++ SDL.forwardedDOMKeys[61] = 1; // plus (expand view)
++ SDL.forwardedDOMKeys[48] = 1; // 0 (return view to normal)
++ SDL.forwardedDOMKeys[84] = 1; // t (new tab)
++ SDL.forwardedDOMKeys[87] = 1; // w (close tab)
++ SDL.forwardedDOMKeys[82] = 1; // r (reload)
+ return 0; // success
+ },
+
diff --git a/src/library.js b/src/library.js
index 0eefea84..d2d61867 100644
--- a/src/library.js
+++ b/src/library.js
@@ -4755,10 +4755,19 @@ LibraryManager.library = {
__cxa_is_number_type: function(type) {
var isNumber = false;
try { if (type == __ZTIi) isNumber = true } catch(e){}
+ try { if (type == __ZTIj) isNumber = true } catch(e){}
try { if (type == __ZTIl) isNumber = true } catch(e){}
+ try { if (type == __ZTIm) isNumber = true } catch(e){}
try { if (type == __ZTIx) isNumber = true } catch(e){}
+ try { if (type == __ZTIy) isNumber = true } catch(e){}
try { if (type == __ZTIf) isNumber = true } catch(e){}
try { if (type == __ZTId) isNumber = true } catch(e){}
+ try { if (type == __ZTIe) isNumber = true } catch(e){}
+ try { if (type == __ZTIc) isNumber = true } catch(e){}
+ try { if (type == __ZTIa) isNumber = true } catch(e){}
+ try { if (type == __ZTIh) isNumber = true } catch(e){}
+ try { if (type == __ZTIs) isNumber = true } catch(e){}
+ try { if (type == __ZTIt) isNumber = true } catch(e){}
return isNumber;
},
@@ -4839,22 +4848,22 @@ LibraryManager.library = {
// RTTI hacks for exception handling, defining type_infos for common types.
// The values are dummies. We simply use the addresses of these statically
// allocated variables as unique identifiers.
- // type_info for int.
- _ZTIi: [0],
- // type_info for long.
- _ZTIl: [0],
- // type_info for long long.
- _ZTIx: [0],
- // type_info for float.
- _ZTIf: [0],
- // type_info for double.
- _ZTId: [0],
- // type_info for char.
- _ZTIc: [0],
- // type_info for void.
- _ZTIv: [0],
- // type_info for void*.
- _ZTIPv: [0],
+ _ZTIi: [0], // int
+ _ZTIj: [0], // unsigned int
+ _ZTIl: [0], // long
+ _ZTIm: [0], // unsigned long
+ _ZTIx: [0], // long long
+ _ZTIy: [0], // unsigned long long
+ _ZTIf: [0], // float
+ _ZTId: [0], // double
+ _ZTIe: [0], // long double
+ _ZTIc: [0], // char
+ _ZTIa: [0], // signed char
+ _ZTIh: [0], // unsigned char
+ _ZTIs: [0], // short
+ _ZTIt: [0], // signed short
+ _ZTIv: [0], // void
+ _ZTIPv: [0], // void*
llvm_uadd_with_overflow_i8: function(x, y) {
x = x & 0xff;
@@ -5669,7 +5678,7 @@ LibraryManager.library = {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html
// NOTE: This is fake, since we can't calculate real CPU time usage in JS.
if (buffer !== 0) {
- memset(buffer, 0, ___tms_struct_layout.__size__);
+ _memset(buffer, 0, ___tms_struct_layout.__size__);
}
return 0;
},
@@ -6295,7 +6304,7 @@ LibraryManager.library = {
htonl: function(value) {
return ((value & 0xff) << 24) + ((value & 0xff00) << 8) +
- ((value & 0xff0000) >> 8) + ((value & 0xff000000) >> 24);
+ ((value & 0xff0000) >>> 8) + ((value & 0xff000000) >>> 24);
},
htons: function(value) {
return ((value & 0xff) << 8) + ((value & 0xff00) >> 8);
diff --git a/src/library_browser.js b/src/library_browser.js
index e6a8b7c6..bf235493 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -10,10 +10,6 @@ mergeInto(LibraryManager.library, {
$Browser: {
mainLoop: {
scheduler: null,
-#if PROFILE_MAIN_LOOP
- meanTime: 0,
- lastReport: 0,
-#endif
shouldPause: false,
paused: false,
queue: [],
@@ -383,8 +379,14 @@ mergeInto(LibraryManager.library, {
emscripten_set_main_loop: function(func, fps) {
Module['noExitRuntime'] = true;
+#if PROFILE_MAIN_LOOP
+ Module['totalTime'] = 0;
+ Module['iterations'] = 0;
+ Module['maxTime'] = 0;
+#endif
+
var jsFunc = FUNCTION_TABLE[func];
- var wrapper = function() {
+ Browser.mainLoop.runner = function() {
if (Browser.mainLoop.queue.length > 0) {
var start = Date.now();
var blocker = Browser.mainLoop.queue.shift();
@@ -402,7 +404,7 @@ mergeInto(LibraryManager.library, {
}
console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers);
Browser.mainLoop.updateStatus();
- setTimeout(wrapper, 0);
+ setTimeout(Browser.mainLoop.runner, 0);
return;
}
if (Browser.mainLoop.shouldPause) {
@@ -415,15 +417,14 @@ mergeInto(LibraryManager.library, {
#if PROFILE_MAIN_LOOP
var start = performance.now();
#endif
+
jsFunc();
+
#if PROFILE_MAIN_LOOP
- var now = performance.now();
- var time = now - start;
- Browser.mainLoop.meanTime = (Browser.mainLoop.meanTime*9 + time)/10;
- if (now - Browser.mainLoop.lastReport > 1000) {
- console.log('main loop time: ' + Browser.mainLoop.meanTime);
- Browser.mainLoop.lastReport = now;
- }
+ var time = performance.now() - start;
+ Module['totalTime'] += time;
+ Module['iterations']++;
+ Module['maxTime'] = Math.max(Module['maxTime'], time);
#endif
if (Browser.mainLoop.shouldPause) {
@@ -436,11 +437,11 @@ mergeInto(LibraryManager.library, {
}
if (fps && fps > 0) {
Browser.mainLoop.scheduler = function() {
- setTimeout(wrapper, 1000/fps); // doing this each time means that on exception, we stop
+ setTimeout(Browser.mainLoop.runner, 1000/fps); // doing this each time means that on exception, we stop
}
} else {
Browser.mainLoop.scheduler = function() {
- Browser.requestAnimationFrame(wrapper);
+ Browser.requestAnimationFrame(Browser.mainLoop.runner);
}
}
Browser.mainLoop.scheduler();
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 48c18c8b..da1e88af 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -18,7 +18,9 @@ var LibrarySDL = {
events: [],
fonts: [null],
+ // The currently preloaded audio elements ready to be played
audios: [null],
+ // The currently playing audio element. There's only one music track.
music: {
audio: null,
volume: 1.0
@@ -346,9 +348,19 @@ var LibrarySDL = {
receiveEvent: function(event) {
switch(event.type) {
case 'mousemove':
- // workaround for firefox bug 750111
- event['movementX'] = event['mozMovementX'];
- event['movementY'] = event['mozMovementY'];
+ if (Browser.pointerLock) {
+ // workaround for firefox bug 750111
+ if ('mozMovementX' in event) {
+ event['movementX'] = event['mozMovementX'];
+ event['movementY'] = event['mozMovementY'];
+ }
+ // workaround for Firefox bug 782777
+ if (event['movementX'] == 0 && event['movementY'] == 0) {
+ // ignore a mousemove event if it doesn't contain any movement info
+ // (without pointer lock, we infer movement from pageX/pageY, so this check is unnecessary)
+ return false;
+ }
+ }
// fall through
case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') {
@@ -394,6 +406,11 @@ var LibrarySDL = {
}
}
break;
+ case 'unload':
+ SDL.events.push(event);
+ // Force-run a main event loop, since otherwise this event will never be caught!
+ Browser.mainLoop.runner();
+ return true;
}
return false;
},
@@ -457,8 +474,14 @@ var LibrarySDL = {
if (Browser.pointerLock) {
// When the pointer is locked, calculate the coordinates
// based on the movement of the mouse.
- var movementX = Browser.getMovementX(event);
- var movementY = Browser.getMovementY(event);
+ // Workaround for Firefox bug 764498
+ if (event.type != 'mousemove' &&
+ ('mozMovementX' in event)) {
+ var movementX = 0, movementY = 0;
+ } else {
+ var movementX = Browser.getMovementX(event);
+ var movementY = Browser.getMovementY(event);
+ }
var x = SDL.mouseX + movementX;
var y = SDL.mouseY + movementY;
} else {
@@ -488,6 +511,10 @@ var LibrarySDL = {
SDL.mouseY = y;
break;
}
+ case 'unload': {
+ {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ break;
+ }
default: throw 'Unhandled SDL event: ' + event.type;
}
},
@@ -506,6 +533,10 @@ var LibrarySDL = {
// Sound
+ // Channels are a SDL abstraction for allowing multiple sound tracks to be
+ // played at the same time. We don't need to actually implement the mixing
+ // since the browser engine handles that for us. Therefore, in JS we just
+ // maintain a list of channels and return IDs for them to the SDL consumer.
allocateChannels: function(num) { // called from Mix_AllocateChannels and init
if (SDL.numChannels && SDL.numChannels >= num) return;
SDL.numChannels = num;
@@ -557,6 +588,7 @@ var LibrarySDL = {
document.onkeydown = SDL.receiveEvent;
document.onkeyup = SDL.receiveEvent;
document.onkeypress = SDL.receiveEvent;
+ window.onunload = SDL.receiveEvent;
SDL.keyboardState = _malloc(0x10000);
_memset(SDL.keyboardState, 0, 0x10000);
// Initialize this structure carefully for closure
@@ -565,6 +597,7 @@ var LibrarySDL = {
SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
+ SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
return 0; // success
},
@@ -640,8 +673,11 @@ var LibrarySDL = {
},
SDL_Quit: function() {
- for (var i = 0; i < SDL.audios; i++) {
- SDL.audios[i].pause();
+ for (var i = 0; i < SDL.numChannels; ++i) {
+ SDL.channels[i].audio.pause();
+ }
+ if (SDL.music.audio) {
+ SDL.music.audio.pause();
}
Module.print('SDL_Quit called (and ignored)');
},
@@ -1015,6 +1051,7 @@ var LibrarySDL = {
// SDL_Audio
+ // TODO fix SDL_OpenAudio, and add some tests for it. It's currently broken.
SDL_OpenAudio: function(desired, obtained) {
SDL.allocateChannels(32);
@@ -1065,6 +1102,7 @@ var LibrarySDL = {
SDL.audio.paused = pauseOn;
},
+ SDL_CloseAudio__deps: ['SDL_PauseAudio', 'free'],
SDL_CloseAudio: function() {
if (SDL.audio) {
_SDL_PauseAudio(1);
@@ -1093,6 +1131,7 @@ var LibrarySDL = {
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
SDL.allocateChannels(32);
+ // Just record the values for a later call to Mix_QuickLoad_RAW
SDL.mixerFrequency = frequency;
SDL.mixerFormat = format;
SDL.mixerNumChannels = channels;
@@ -1137,6 +1176,7 @@ var LibrarySDL = {
Module["preloadedAudios"][filename] = null;
}
var id = SDL.audios.length;
+ // Keep the loaded audio in the audio arrays, ready for playback
SDL.audios.push({
source: filename,
audio: raw
@@ -1146,12 +1186,15 @@ var LibrarySDL = {
Mix_QuickLoad_RAW: function(mem, len) {
var audio = new Audio();
- audio['mozSetup'](SDL.mixerNumChannels, SDL.mixerFrequency);
- var numSamples = (len / (SDL.mixerNumChannels * 2)) | 0;
+ // Record the number of channels and frequency for later usage
+ audio.numChannels = SDL.mixerNumChannels;
+ audio.frequency = SDL.mixerFrequency;
+ var numSamples = len >> 1; // len is the length in bytes, and the array contains 16-bit PCM values
var buffer = new Float32Array(numSamples);
for (var i = 0; i < numSamples; ++i) {
buffer[i] = ({{{ makeGetValue('mem', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
}
+ // FIXME: doesn't make sense to keep the audio element in the buffer
var id = SDL.audios.length;
SDL.audios.push({
source: '',
@@ -1167,10 +1210,15 @@ var LibrarySDL = {
Mix_PlayChannel: function(channel, id, loops) {
// TODO: handle loops
+
+ // Get the audio element associated with the ID
var info = SDL.audios[id];
if (!info) return 0;
var audio = info.audio;
if (!audio) return 0;
+
+ // If the user asks us to allocate a channel automatically, get the first
+ // free one.
if (channel == -1) {
channel = 0;
for (var i = 0; i < SDL.numChannels; i++) {
@@ -1180,6 +1228,9 @@ var LibrarySDL = {
}
}
}
+
+ // We clone the audio node to utilize the preloaded audio buffer, since
+ // the browser has already preloaded the audio file.
var channelInfo = SDL.channels[channel];
channelInfo.audio = audio = audio.cloneNode(true);
if (SDL.channelFinished) {
@@ -1187,9 +1238,55 @@ var LibrarySDL = {
Runtime.getFuncWrapper(SDL.channelFinished)(channel);
}
}
+ // Either play the element, or load the dynamic data into it
if (info.buffer) {
- audio['mozSetup'](SDL.mixerNumChannels, SDL.mixerFrequency);
- audio["mozWriteAudio"](info.buffer);
+ var contextCtor = null;
+ if (audio && ('mozSetup' in audio)) { // Audio Data API
+ try {
+ audio['mozSetup'](audio.numChannels, audio.frequency);
+ audio["mozWriteAudio"](info.buffer);
+ } catch (e) {
+ // Workaround for Firefox bug 783052
+ // ignore this exception!
+ }
+ /*
+ } else if (contextCtor = (window.AudioContext || // WebAudio API
+ window.webkitAudioContext)) {
+ var currentIndex = 0;
+ var numChannels = parseInt(audio.numChannels);
+ var context = new contextCtor();
+ var source = context.createBufferSource();
+ source.loop = false;
+ source.buffer = context.createBuffer(numChannels, 1, audio.frequency);
+ var jsNode = context.createJavaScriptNode(2048, numChannels, numChannels);
+ jsNode.onaudioprocess = function(event) {
+ var buffers = new Array(numChannels);
+ for (var i = 0; i < numChannels; ++i) {
+ buffers[i] = event.outputBuffer.getChannelData(i);
+ }
+ var remaining = info.buffer.length - currentIndex;
+ if (remaining > 2048) {
+ remaining = 2048;
+ }
+ for (var i = 0; i < remaining;) {
+ for (var j = 0; j < numChannels; ++j) {
+ buffers[j][i] = info.buffer[currentIndex + i + j] * audio.volume;
+ }
+ i += j;
+ }
+ currentIndex += remaining * numChannels;
+ for (var i = remaining; i < 2048;) {
+ for (var j = 0; j < numChannels; ++j) {
+ buffers[j][i] = 0; // silence
+ }
+ i += j;
+ }
+ };
+ source.connect(jsNode);
+ jsNode.connect(context.destination);
+ source.noteOn(0);
+ */
+ }
} else {
audio.play();
}
diff --git a/src/postamble.js b/src/postamble.js
index e1dd495a..d164f049 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -32,14 +32,21 @@ Module.callMain = function callMain(args) {
function run(args) {
args = args || Module['arguments'];
+ if (runDependencies > 0) {
+ Module.printErr('run() called, but dependencies remain, so not running');
+ return 0;
+ }
+
if (Module['preRun']) {
if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
- while (Module['preRun'].length > 0) {
- Module['preRun'].pop()();
- if (runDependencies > 0) {
- // preRun added a dependency, run will be called later
- return 0;
- }
+ var toRun = Module['preRun'];
+ Module['preRun'] = [];
+ for (var i = toRun.length-1; i >= 0; i--) {
+ toRun[i]();
+ }
+ if (runDependencies > 0) {
+ // a preRun added a dependency, run will be called later
+ return 0;
}
}
@@ -89,14 +96,15 @@ if (Module['preInit']) {
initRuntime();
#if INVOKE_RUN
+var shouldRunNow = true;
#else
-addRunDependency();
+var shouldRunNow = false;
#endif
if (Module['noInitialRun']) {
- addRunDependency();
+ shouldRunNow = false;
}
-if (runDependencies == 0) {
+if (shouldRunNow) {
var ret = run();
#if CATCH_EXIT_CODE
Module.print('Exit Status: ' + ret);
diff --git a/src/settings.js b/src/settings.js
index cf329568..9f63622d 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -5,6 +5,7 @@
//
// emcc -s OPTION1=VALUE1 -s OPTION2=VALUE2 [..other stuff..]
//
+// See https://github.com/kripken/emscripten/wiki/Code-Generation-Modes/
// Tuning
var QUANTUM_SIZE = 4; // This is the size of an individual field in a structure. 1 would
@@ -51,15 +52,10 @@ var FAST_MEMORY = 2*1024*1024; // The amount of memory to initialize to 0. This
// Code embetterments
var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables
var RELOOP = 0; // Recreate js native loops from llvm data
-var USE_TYPED_ARRAYS = 2; // Use typed arrays for the heap
+var USE_TYPED_ARRAYS = 2; // Use typed arrays for the heap. See https://github.com/kripken/emscripten/wiki/Code-Generation-Modes/
// 1 has two heaps, IHEAP (int32) and FHEAP (double),
- // and addresses there are a match for normal addresses. This wastes memory but can be fast.
- // 2 is a single heap, accessible through views as int8, int32, etc. This saves memory but
- // has more overhead of pointer calculations. It also is limited to storing doubles as floats,
- // simply because double stores are not necessarily 64-bit aligned, and we can only access
- // 64-bit aligned values with a 64-bit typed array. Likewise int64s are stored as int32's,
- // which is potentially very dangerous!
- // TODO: require compiling with -malign-double, which does align doubles
+ // and addresses there are a match for normal addresses.
+ // 2 is a single heap, accessible through views as int8, int32, etc.
var USE_FHEAP = 1; // Relevant in USE_TYPED_ARRAYS == 1. If this is disabled, only IHEAP will be used, and FHEAP
// not generated at all. This is useful if your code is 100% ints without floats or doubles
var DOUBLE_MODE = 1; // How to load and store 64-bit doubles. Without typed arrays or in typed array mode 1,