diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rwxr-xr-x | emcc | 230 | ||||
-rw-r--r-- | src/library.js | 20 | ||||
-rw-r--r-- | src/library_gl.js | 7 | ||||
-rw-r--r-- | src/library_sdl.js | 101 | ||||
-rw-r--r-- | src/parseTools.js | 3 | ||||
-rw-r--r-- | src/postamble.js | 4 | ||||
-rw-r--r-- | src/preamble.js | 4 | ||||
-rw-r--r-- | src/shell.html | 30 | ||||
-rw-r--r-- | system/include/libc/stdio.h | 9 | ||||
-rw-r--r-- | tests/cases/uadd_overflow.ll | 25 | ||||
-rw-r--r-- | tests/cases/uadd_overflow.txt | 1 | ||||
-rw-r--r-- | tests/freetype/main_2.c | 135 | ||||
-rw-r--r-- | tests/freetype/ref_2.txt | 33 | ||||
-rw-r--r-- | tests/hello_world_sdl.cpp | 4 | ||||
-rwxr-xr-x | tests/runner.py | 204 | ||||
-rw-r--r-- | tests/sdl_audio.c | 50 | ||||
-rw-r--r-- | tests/sdl_canvas.c | 8 | ||||
-rw-r--r-- | tests/sdl_mouse.c | 2 | ||||
-rw-r--r-- | tests/sounds/LICENSE.txt | 19 | ||||
-rw-r--r-- | tests/sounds/alarmcreatemiltaryfoot_1.wav | bin | 0 -> 443856 bytes | |||
-rw-r--r-- | tests/sounds/alarmvictory_1.ogg | bin | 0 -> 56490 bytes | |||
-rw-r--r-- | tools/eliminator/eliminator.coffee | 15 | ||||
-rw-r--r-- | tools/shared.py | 28 | ||||
-rw-r--r-- | tools/test-js-optimizer-output.js | 5 | ||||
-rw-r--r-- | tools/test-js-optimizer.js | 6 |
26 files changed, 721 insertions, 223 deletions
@@ -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> @@ -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, "&"); - text = text.replace(/</g, "<"); - text = text.replace(/>/g, ">"); - text = text.replace('\n', '<br>', 'g'); |