aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rwxr-xr-xemcc230
-rw-r--r--src/library.js20
-rw-r--r--src/library_gl.js7
-rw-r--r--src/library_sdl.js101
-rw-r--r--src/parseTools.js3
-rw-r--r--src/postamble.js4
-rw-r--r--src/preamble.js4
-rw-r--r--src/shell.html30
-rw-r--r--system/include/libc/stdio.h9
-rw-r--r--tests/cases/uadd_overflow.ll25
-rw-r--r--tests/cases/uadd_overflow.txt1
-rw-r--r--tests/freetype/main_2.c135
-rw-r--r--tests/freetype/ref_2.txt33
-rw-r--r--tests/hello_world_sdl.cpp4
-rwxr-xr-xtests/runner.py204
-rw-r--r--tests/sdl_audio.c50
-rw-r--r--tests/sdl_canvas.c8
-rw-r--r--tests/sdl_mouse.c2
-rw-r--r--tests/sounds/LICENSE.txt19
-rw-r--r--tests/sounds/alarmcreatemiltaryfoot_1.wavbin0 -> 443856 bytes
-rw-r--r--tests/sounds/alarmvictory_1.oggbin0 -> 56490 bytes
-rw-r--r--tools/eliminator/eliminator.coffee15
-rw-r--r--tools/shared.py28
-rw-r--r--tools/test-js-optimizer-output.js5
-rw-r--r--tools/test-js-optimizer.js6
26 files changed, 721 insertions, 223 deletions
diff --git a/AUTHORS b/AUTHORS
index 248c345d..145e3812 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,3 +16,4 @@ under the licensing terms detailed in LICENSE.
* Adrian Taylor <adrian@macrobug.com>
* Richard Assar <richard.assar@gmail.com>
* Nathan Hammond <emscripten@nathanhammond.com>
+* Behdad Esfahbod <behdad@behdad.org>
diff --git a/emcc b/emcc
index 044347b7..a6cacb39 100755
--- a/emcc
+++ b/emcc
@@ -74,7 +74,7 @@ emcc can be influenced by a few environment variables:
EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang.
'''
-import os, sys, shutil, tempfile, subprocess
+import os, sys, shutil, tempfile, subprocess, shlex
from subprocess import PIPE, STDOUT
from tools import shared
@@ -174,6 +174,12 @@ Options that are modified or new in %s include:
2: -O2 LLVM optimizations
3: -O3 LLVM optimizations (default in -O2+)
+ --llvm-lto <level> 0: No LLVM LTO (default in -O0)
+ 1: LLVM LTO (default in -O1+)
+ Note: If LLVM optimizations are not run
+ (see --llvm-opts), setting this to 1 has no
+ effect.
+
--closure <on> 0: No closure compiler (default in -O0, -O1)
1: Run closure compiler (default in -O2, -O3)
@@ -232,6 +238,10 @@ Options that are modified or new in %s include:
receive an array/typed array and return
an array/typed array.
Compression only works when generating HTML.
+ When compression is on, all filed specified
+ to be preloaded are compressed in one big
+ archive, which is given the same name as the
+ output HTML but with suffix .data.compress
--minify <on> 0: Do not minify the generated JavaScript's
whitespace (default if closure compiler
@@ -307,7 +317,7 @@ if os.environ.get('EMMAKEN_CXX'):
CC_ADDITIONAL_ARGS = shared.COMPILER_OPTS # + ['-g']?
EMMAKEN_CFLAGS = os.environ.get('EMMAKEN_CFLAGS')
-if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += EMMAKEN_CFLAGS.split(' ')
+if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS)
# ---------------- Utilities ---------------
@@ -319,9 +329,11 @@ ASSEMBLY_SUFFIXES = ('.ll',)
LIB_PREFIXES = ('', 'lib')
IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp')
+AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3')
+AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' }
def suffix(name):
- return name.split('.')[:-1]
+ return name.split('.')[-1]
def unsuffixed(name):
return '.'.join(name.split('.')[:-1])
@@ -347,6 +359,12 @@ for i in range(1, len(sys.argv)):
if arg.endswith('.h') and sys.argv[i-1] != '-include':
header = True
+if '-M' in sys.argv or '-MM' in sys.argv:
+ # Just output dependencies, do not compile. Warning: clang and gcc behave differently with -MF! (clang seems to not recognize it)
+ cmd = [CC] + sys.argv[1:]
+ if DEBUG: print >> sys.stderr, 'emcc, just dependencies: ', ' '.join(cmd)
+ exit(subprocess.call(cmd))
+
# Check if a target is specified
target = None
for i in range(len(sys.argv)-1):
@@ -401,6 +419,7 @@ try:
opt_level = 0
llvm_opts = None
+ llvm_lto = None
closure = None
js_transform = None
pre_js = None
@@ -427,6 +446,11 @@ try:
llvm_opts = eval(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
+ elif newargs[i].startswith('--llvm-lto'):
+ check_bad_eq(newargs[i])
+ llvm_lto = eval(newargs[i+1])
+ newargs[i] = ''
+ newargs[i+1] = ''
elif newargs[i].startswith('--closure'):
check_bad_eq(newargs[i])
closure = int(newargs[i+1])
@@ -474,12 +498,6 @@ try:
Compression.on = True
newargs[i] = ''
newargs[i+1] = ''
- elif newargs[i] == '-MF': # clang cannot handle this, so we fake it
- f = open(newargs[i+1], 'w')
- f.write('\n')
- f.close()
- newargs[i] = ''
- newargs[i+1] = ''
elif newargs[i] == '--ignore-dynamic-linking':
ignore_dynamic_linking = True
newargs[i] = ''
@@ -491,6 +509,7 @@ try:
newargs = [ arg for arg in newargs if arg is not '' ]
if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level]
+ if llvm_lto is None: llvm_lto = llvm_opts > 0
if closure is None: closure = 1 if opt_level >= 2 else 0
if minify_whitespace is None:
minify_whitespace = closure # if closure is run, minify whitespace
@@ -776,7 +795,7 @@ try:
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts)
if DEBUG: save_intermediate('opt', 'bc')
# Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript)
- if shared.Building.can_use_unsafe_opts() and shared.Building.can_build_standalone():
+ if llvm_lto and shared.Building.can_use_unsafe_opts() and shared.Building.can_build_standalone():
lto_opts = []
if not shared.Building.can_inline(): lto_opts.append('-disable-inlining')
lto_opts.append('-std-link-opts')
@@ -813,6 +832,23 @@ try:
if DEBUG: print >> sys.stderr, 'emcc: setting up files'
code = ''
+ if final_suffix == 'html':
+ code += '''
+ var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs"));
+ var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");
+ var hasBlobConstructor;
+ try {
+ new Blob();
+ hasBlobConstructor = true;
+ } catch(e) {
+ hasBlobConstructor = false;
+ console.log("warning: no blob constructor, cannot create blobs with mimetypes");
+ }
+'''
+
+ code += 'var preloadedImages = {}; // maps url to image data\n'
+ code += 'var preloadedAudios = {}; // maps url to audio data\n'
+
# Expand directories into individual files
def add(mode, dirname, names):
for name in names:
@@ -827,19 +863,8 @@ try:
for file_ in data_files:
file_['net_name'] = file_['name']
- file_['compressed'] = False
- if Compression.on:
- # Compress each file, if it is worth it
- for file_ in data_files:
- Compression.compress(file_['name'])
- if Compression.worth_it(os.stat(file_['name']).st_size,
- os.stat(Compression.compressed_name(file_['name'])).st_size):
- file_['net_name'] = Compression.compressed_name(file_['name'])
- file_['compressed'] = True
- else:
- if DEBUG: print >> sys.stderr, 'emcc: not compressing %s since not worth it' % file_['name']
- os.remove(Compression.compressed_name(file_['name']))
+ data_target = unsuffixed(target) + '.data'
# Set up folders
partial_dirs = []
@@ -853,8 +878,31 @@ try:
code += '''FS.createFolder('/%s', '%s', true, false);\n''' % (os.path.sep.join(parts[:i]), parts[i])
partial_dirs.append(partial)
- code += 'var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs"));\n'
- code += 'var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");\n'
+ if final_suffix == 'html':
+ # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead.
+ data = open(data_target, 'wb')
+ start = 0
+ for file_ in data_files:
+ file_['data_start'] = start
+ curr = open(file_['name']).read()
+ file_['data_end'] = start + len(curr)
+ start += len(curr)
+ data.write(curr)
+ data.close()
+ if Compression.on:
+ Compression.compress(data_target)
+
+ # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs
+ code += '''
+ function DataRequest() {}
+ DataRequest.prototype = {
+ requests: {},
+ open: function(mode, name) {
+ this.requests[name] = this;
+ },
+ send: function() {}
+ };
+ '''
counter = 0
for file_ in data_files:
@@ -864,56 +912,121 @@ try:
code += '''FS.createDataFile('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(filename, 'rb').read())))
elif file_['mode'] == 'preload':
# Preload
+ assert final_suffix == 'html', 'Can only preload files when generating HTML'
+
varname = 'filePreload%d' % counter
counter += 1
image = filename.endswith(IMAGE_SUFFIXES)
+ audio = filename.endswith(AUDIO_SUFFIXES)
+
+ if image:
+ finish = '''
+ var bb = new BlobBuilder();
+ bb.append(byteArray.buffer);
+ var b = bb.getBlob();
+ var url = URLObject.createObjectURL(b);
+ var img = new Image();
+ img.onload = function() {
+ assert(img.complete, 'Image %(filename)s could not be decoded');
+ var canvas = document.createElement('canvas');
+ canvas.width = img.width;
+ canvas.height = img.height;
+ var ctx = canvas.getContext('2d');
+ ctx.drawImage(img, 0, 0);
+ preloadedImages['%(filename)s'] = canvas;
+ URLObject.revokeObjectURL(url);
+ removeRunDependency();
+ };
+ img.onerror = function(event) {
+ console.log('Image %(filename)s could not be decoded');
+ };
+ img.src = url;
+''' % { 'filename': filename }
+ elif audio:
+ # Need actual blob constructor here, to set the mimetype or else audios fail to decode
+ finish = '''
+ if (hasBlobConstructor) {
+ var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' });
+ var url = URLObject.createObjectURL(b); // XXX we never revoke this!
+ var audio = new Audio();
+ audio['oncanplaythrough'] = function() { // XXX string for closure
+ audio['oncanplaythrough'] = null;
+ preloadedAudios['%(filename)s'] = audio;
+ removeRunDependency();
+ };
+ audio.onerror = function(event) {
+ console.log('Audio %(filename)s could not be decoded');
+ };
+ audio.src = url;
+ } else {
+ preloadedAudios['%(filename)s'] = new Audio(); // empty shim
+ removeRunDependency();
+ }
+''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] }
+ else:
+ finish = 'removeRunDependency();\n'
+
code += '''
- var %(varname)s = new XMLHttpRequest();
+ var %(varname)s = new %(request)s();
%(varname)s.open('GET', '%(netname)s', true);
%(varname)s.responseType = 'arraybuffer';
%(varname)s.onload = function() {
- var arrayBuffer = %(varname)s.response; // Note: not X.responseText
+ var arrayBuffer = %(varname)s.response;
assert(arrayBuffer, 'Loading file %(filename)s failed.');
- var byteArray = new Uint8Array(arrayBuffer);
- %(decompress_start)s
+ var byteArray = arrayBuffer.byteLength ? new Uint8Array(arrayBuffer) : arrayBuffer;
FS.createDataFile('/%(dirname)s', '%(basename)s', byteArray, true, true);
%(finish)s
- %(decompress_end)s
};
addRunDependency();
%(varname)s.send(null);
''' % {
+ 'request': 'DataRequest', # In the past we also supported XHRs here
'varname': varname,
'filename': filename,
'netname': file_['net_name'],
'dirname': os.path.dirname(filename),
'basename': os.path.basename(filename),
- 'decompress_start': '' if not file_['compressed'] else 'Module["decompress"](byteArray, function(decompressed) { byteArray = new Uint8Array(decompressed);',
- 'decompress_end': '' if not file_['compressed'] else '});',
- 'finish': 'removeRunDependency();' if not image else '''var bb = new BlobBuilder();
- bb.append(byteArray.buffer);
- var b = bb.getBlob();
- var url = URLObject.createObjectURL(b);
- var img = new Image();
- img.onload = function() {
- assert(img.complete, 'Image %(filename)s could not be decoded');
- var canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- preloadedImages['%(filename)s'] = canvas;
- URLObject.revokeObjectURL(url);
- removeRunDependency();
- };
- img.onerror = function(event) {
- console.log('Image %(filename)s could not be decoded: ' + JSON.stringify(event));
- };
- img.src = url;
-''' % { 'filename': filename }
+ 'finish': finish
}
else:
assert 0
+
+ if final_suffix == 'html':
+ # Get the big archive and split it up
+ use_data = ''
+ for file_ in data_files:
+ if file_['mode'] == 'preload':
+ use_data += '''
+ curr = DataRequest.prototype.requests['%s'];
+ curr.response = byteArray.subarray(%d,%d);
+ curr.onload();
+ ''' % (file_['name'], file_['data_start'], file_['data_end'])
+ use_data += ' removeRunDependency();\n'
+
+ if Compression.on:
+ use_data = '''
+ Module["decompress"](byteArray, function(decompressed) {
+ byteArray = new Uint8Array(decompressed);
+ %s
+ });
+ ''' % use_data
+
+ code += '''
+ var dataFile = new XMLHttpRequest();
+ dataFile.open('GET', '%s', true);
+ dataFile.responseType = 'arraybuffer';
+ dataFile.onload = function() {
+ var arrayBuffer = dataFile.response;
+ assert(arrayBuffer, 'Loading data file failed.');
+ var byteArray = new Uint8Array(arrayBuffer);
+ var curr;
+ %s
+ };
+ addRunDependency();
+ dataFile.send(null);
+ if (Module['setStatus']) Module['setStatus']('Downloading...');
+ ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data)
+
src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', code)
final += '.files.js'
open(final, 'w').write(src)
@@ -932,7 +1045,7 @@ try:
shutil.copyfile(final, final + '.tr.js')
final += '.tr.js'
if DEBUG: print >> sys.stderr, 'emcc: applying transform: %s' % js_transform
- execute(js_transform.split(' ') + [os.path.abspath(final)])
+ execute(shlex.split(js_transform) + [os.path.abspath(final)])
if DEBUG: save_intermediate('transformed')
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
@@ -1013,14 +1126,25 @@ try:
decoding = '''
var decompressWorker = new Worker('decompress.js');
var decompressCallbacks = [];
+ var decompressions = 0;
Module["decompress"] = function(data, callback) {
var id = decompressCallbacks.length;
decompressCallbacks.push(callback);
decompressWorker.postMessage({ data: data, id: id });
+ if (Module['setStatus']) {
+ decompressions++;
+ Module['setStatus']('Decompressing...');
+ }
};
decompressWorker.onmessage = function(event) {
decompressCallbacks[event.data.id](event.data.data);
decompressCallbacks[event.data.id] = null;
+ if (Module['setStatus']) {
+ decompressions--;
+ if (decompressions == 0) {
+ Module['setStatus']('');
+ }
+ }
};
var compiledCodeXHR = new XMLHttpRequest();
compiledCodeXHR.open('GET', '%s', true);
diff --git a/src/library.js b/src/library.js
index aa1fd242..78dd629b 100644
--- a/src/library.js
+++ b/src/library.js
@@ -445,6 +445,14 @@ LibraryManager.library = {
standardizePath: function(path) {
if (path.substr(0, 2) == './') path = path.substr(2);
return path;
+ },
+
+ deleteFile: function(path) {
+ var path = FS.analyzePath(path);
+ if (!path.parentExists || !path.exists) {
+ throw 'Invalid path ' + path;
+ }
+ delete path.parentObject.contents[path.name];
}
},
@@ -4642,16 +4650,20 @@ LibraryManager.library = {
_ZTIPv: [0],
llvm_uadd_with_overflow_i32: function(x, y) {
+ x = x>>>0;
+ y = y>>>0;
return {
- f0: x+y,
- f1: 0 // We never overflow... for now
+ f0: (x+y)>>>0,
+ f1: x+y > 4294967295
};
},
llvm_umul_with_overflow_i32: function(x, y) {
+ x = x>>>0;
+ y = y>>>0;
return {
- f0: x*y,
- f1: 0 // We never overflow... for now
+ f0: (x*y)>>>0,
+ f1: x*y > 4294967295
};
},
diff --git a/src/library_gl.js b/src/library_gl.js
index 6b0a270f..f6fb2e27 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -1,6 +1,11 @@
//"use strict";
-// XXX FIXME Hardcoded '4' in many places, here and in library_SDL, for RGBA
+// FIXME:
+// * glGetUniformLocation should return -1 when the value is not valid, not null
+// * glUniform1fi should be glUniform1iv
+// * glGetAttribLocation lacks return value (and should be -1 when not valid)
+// * single-underscore deps need double underscore (and, just auto-add them all)
+// * glGetProgramInfoLog and *shader* should be essentially identical
var LibraryGL = {
$GL: {
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 06e10339..631de481 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -175,17 +175,21 @@ mergeInto(LibraryManager.library, {
},
// Load SDL color into a CSS-style color specification
- loadColorToCSS: function(color) {
+ loadColorToCSSRGB: function(color) {
var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
- return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (1-((rgba >> 24)&255)/255) + ')';
+ return 'rgb(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ')';
+ },
+ loadColorToCSSRGBA: function(color) {
+ var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
+ return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (((rgba >> 24)&255)/255) + ')';
},
- translateColorToCSS: function(rgba) {
- return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + (1-(rgba&255)/255) + ')';
+ translateColorToCSSRGBA: function(rgba) {
+ return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
},
- translateRGBAToCSS: function(r, g, b, a) {
- return 'rgba(' + r + ',' + g + ',' + b + ',' + (1-a/255) + ')';
+ translateRGBAToCSSRGBA: function(r, g, b, a) {
+ return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
},
makeSurface: function(width, height, flags, usePageCanvas, source) {
@@ -273,6 +277,11 @@ mergeInto(LibraryManager.library, {
switch(event.type) {
case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'mousemove':
SDL.events.push(event);
+ if ((event.keyCode >= 37 && event.keyCode <= 40) || // arrow keys
+ event.keyCode == 32 || // space
+ event.keyCode == 33 || event.keyCode == 34) { // page up/down
+ event.preventDefault();
+ }
break;
}
//event.preventDefault();
@@ -342,7 +351,7 @@ mergeInto(LibraryManager.library, {
var tempCtx = SDL.surfaces[SDL.screen].ctx;
tempCtx.save();
tempCtx.font = fontString;
- var ret = tempCtx.measureText(text).width;
+ var ret = tempCtx.measureText(text).width | 0;
tempCtx.restore();
return ret;
},
@@ -408,6 +417,9 @@ mergeInto(LibraryManager.library, {
},
SDL_Quit: function() {
+ for (var i = 0; i < SDL.audios; i++) {
+ SDL.audios[i].pause();
+ }
Module.print('SDL_Quit called (and ignored)');
},
@@ -459,13 +471,14 @@ mergeInto(LibraryManager.library, {
assert(buffer % 4 == 0, 'Invalid buffer offset: ' + buffer);
var src = buffer >> 2;
var dst = 0;
+ var isScreen = surf == SDL.screen;
while (dst < num) {
// TODO: access underlying data buffer and write in 32-bit chunks or more
var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
- data[dst] = val & 0xff;
+ data[dst ] = val & 0xff;
data[dst+1] = (val >> 8) & 0xff;
data[dst+2] = (val >> 16) & 0xff;
- data[dst+3] = (val >> 24) & 0xff;
+ data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
src++;
dst += 4;
}
@@ -582,14 +595,14 @@ mergeInto(LibraryManager.library, {
assert(!surfData.locked); // but we could unlock and re-lock if we must..
var r = SDL.loadRect(rect);
surfData.ctx.save();
- surfData.ctx.fillStyle = SDL.translateColorToCSS(color);
+ surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color);
surfData.ctx.fillRect(r.x, r.y, r.w, r.h);
surfData.ctx.restore();
},
SDL_BlitSurface__deps: ['SDL_UpperBlit'],
SDL_BlitSurface: function(src, srcrect, dst, dstrect) {
- return _SDL_Blit(src, srcrect, dst, dstrect);
+ return _SDL_UpperBlit(src, srcrect, dst, dstrect);
},
SDL_SetAlpha: function(surf, flag, alpha) {
@@ -716,15 +729,14 @@ mergeInto(LibraryManager.library, {
SDL_CondWait: function() {},
SDL_DestroyCond: function() {},
-//SDL_CreateYUVOverlay
-//SDL_CreateThread, SDL_WaitThread etc
-
// SDL Mixer
- Mix_OpenAudio: function() { return 0 },
+ Mix_OpenAudio: function(frequency, format, channels, chunksize) {
+ return 0;
+ },
Mix_HookMusicFinished: function(func) {
- SDL.hookMusicFinished = func; // TODO: use this
+ SDL.hookMusicFinished = func;
},
Mix_VolumeMusic: function(func) {
@@ -733,46 +745,39 @@ mergeInto(LibraryManager.library, {
Mix_LoadWAV_RW: function(filename, freesrc) {
filename = FS.standardizePath(Pointer_stringify(filename));
+ var raw = preloadedAudios[filename];
+ assert(raw, 'Cannot find preloaded audio ' + filename);
var id = SDL.audios.length;
SDL.audios.push({
- audio: new Audio(filename)
+ source: filename,
+ audio: raw
});
return id;
},
- Mix_FreeChunk: function(audio) {
+ Mix_FreeChunk: function(id) {
SDL.audios[id].audio.pause();
SDL.audios[id] = null;
- return 0;
},
- Mix_PlayChannel: function(channel, audio, loops) {
+ Mix_PlayChannel: function(channel, id, loops) {
+ // TODO: handle loops
var audio = SDL.audios[id].audio;
+ if (audio.currentTime) audio.src = audio.src; // This hack prevents lags on replaying // TODO: parallel sounds through //cloneNode(true).play()
audio.play();
- return 0; // XXX should return channel
+ return 1; // XXX should return channel
},
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
- Mix_LoadMUS: function(filename) {
- filename = FS.standardizePath(Pointer_stringify(filename));
- var id = SDL.audios.length;
- SDL.audios.push({
- audio: new Audio(filename)
- });
- return id;
- },
-
- Mix_FreeMusic: function(id) {
- SDL.audios[id].audio.pause();
- SDL.audios[id] = null;
- return 0;
- },
+ Mix_LoadMUS: 'Mix_LoadWAV_RW',
+ Mix_FreeMusic: 'Mix_FreeChunk',
Mix_PlayMusic: function(id, loops) {
- if (loops == 0) return;
+ loops = Math.max(loops, 1);
var audio = SDL.audios[id].audio;
- audio.loop = loop != 1; // TODO: handle N loops for finite N
+ audio.loop = loops != 1; // TODO: handle N loops for finite N
audio.play();
+ SDL.music = audio;
return 0;
},
@@ -788,15 +793,21 @@ mergeInto(LibraryManager.library, {
return 0;
},
- Mix_HaltMusic: function(id) {
- var audio = SDL.audios[id].audio;
- audio.pause(); // TODO: actually rewind to the beginning
+ Mix_HaltMusic: function() {
+ var audio = SDL.music;
+ if (!audio) return 0;
+ audio.src = audio.src; // rewind
+ audio.pause();
+ SDL.music = null;
+ if (SDL.hookMusicFinished) {
+ FUNCTION_TABLE[SDL.hookMusicFinished]();
+ }
return 0;
},
Mix_FadeInMusicPos: 'Mix_PlayMusic', // XXX ignore fading in effect
- Mix_FadeOutMusic: function(id) {}, // TODO
+ Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect
// SDL TTF
@@ -818,7 +829,7 @@ mergeInto(LibraryManager.library, {
var fontData = SDL.fonts[font];
var w = SDL.estimateTextWidth(fontData, text);
var h = fontData.size;
- var color = SDL.loadColorToCSS(color);
+ var color = SDL.loadColorToCSSRGB(color); // XXX alpha breaks fonts?
var fontString = h + 'px sans-serif';
var surf = SDL.makeSurface(w, h, 0, false, 'text:' + text); // bogus numbers..
var surfData = SDL.surfaces[surf];
@@ -861,7 +872,7 @@ mergeInto(LibraryManager.library, {
assert(!surfData.locked); // but we could unlock and re-lock if we must..
// TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
surfData.ctx.save();
- surfData.ctx.fillStyle = SDL.translateRGBAToCSS(r, g, b, a);
+ surfData.ctx.fillStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
surfData.ctx.restore();
},
@@ -870,7 +881,7 @@ mergeInto(LibraryManager.library, {
var surfData = SDL.surfaces[surf];
assert(!surfData.locked); // but we could unlock and re-lock if we must..
surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSS(r, g, b, a);
+ surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
surfData.ctx.restore();
},
@@ -879,7 +890,7 @@ mergeInto(LibraryManager.library, {
var surfData = SDL.surfaces[surf];
assert(!surfData.locked); // but we could unlock and re-lock if we must..
surfData.ctx.save();
- surfData.ctx.strokeStyle = SDL.translateRGBAToCSS(r, g, b, a);
+ surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.beginPath();
surfData.ctx.moveTo(x1, y1);
surfData.ctx.lineTo(x2, y2);
diff --git a/src/parseTools.js b/src/parseTools.js
index 520d278e..0e6ddee8 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1537,7 +1537,8 @@ function makeRounding(value, bits, signed, floatConversion) {
// TODO: handle roundings of i64s
assert(bits);
// C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
- if (bits <= 32 && signed) return '((' + value + ')|0)'; // This is fast and even correct, for all cases
+ if (bits <= 32 && signed) return '((' + value + ')&-1)'; // This is fast and even correct, for all cases. Note that it is the same
+ // as |0, but &-1 hints to the js optimizer that this is a rounding correction
// Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure
// the value is non-negative
if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')';
diff --git a/src/postamble.js b/src/postamble.js
index 62966d61..b14a31a1 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -32,6 +32,10 @@ Module.callMain = function callMain(args) {
function run(args) {
args = args || Module['arguments'];
+ if (Module['setStatus']) {
+ Module['setStatus'](''); // clear the status from "Downloading.." etc.
+ }
+
if (Module['preRun']) {
Module['preRun']();
}
diff --git a/src/preamble.js b/src/preamble.js
index 86a5ce80..c88a4671 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -850,9 +850,5 @@ function removeRunDependency() {
if (runDependencies == 0) run();
}
-// Preloading
-
-var preloadedImages = {}; // maps url to image data
-
// === Body ===
diff --git a/src/shell.html b/src/shell.html
index 79b7e9b9..436ba37e 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -4,34 +4,36 @@
<body>
<center>
<canvas id='canvas' width='256' height='256'></canvas>
+ <hr>
+ <textarea id="output" style="font-family: monospace; width: 80%" rows="8"></textarea>
+ <hr>
+ <div id='status'>Downloading...</div>
</center>
<hr>
- <div id='output'></div>
- <hr>
- <center><div id='status'></div></center>
- <hr>
<script type='text/javascript'>
// connect to canvas
var Module = {
print: (function() {
var element = document.getElementById('output');
- var printBuffer = [];
+ element.value = ''; // clear browser cache
return function(text) {
- text = text.replace(/&/g, "&amp;");
- text = text.replace(/</g, "&lt;");
- text = text.replace(/>/g, "&gt;");
- text = text.replace('\n', '<br>', 'g');