Skip to content

Instantly share code, notes, and snippets.

@ajbkr
Created June 15, 2012 18:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajbkr/2938105 to your computer and use it in GitHub Desktop.
Save ajbkr/2938105 to your computer and use it in GitHub Desktop.
library_sdl.js including circleRGBA() function
#include <SDL/SDL.h>
#include <SDL/SDL_gfxPrimitives.h>
int main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* surf = SDL_SetVideoMode(320, 200, 32, SDL_SWSURFACE);
circleRGBA(surf, 160, 100, 100, 255, 255, 255, 255);
SDL_Flip(surf);
SDL_Quit();
return 0;
}
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
canvas.emscripten { border: 1px solid black; }
textarea.emscripten { font-family: monospace; width: 80%; }
div.emscripten { text-align: center; }
</style>
</head>
<body>
<hr/>
<div class="emscripten" id="status">Downloading...</div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<hr/>
<div class="emscripten"><input type="button" value="fullscreen" onclick="Module.requestFullScreen()"></div>
<hr/>
<textarea class="emscripten" id="output" rows="8"></textarea>
<hr>
<script type='text/javascript'>
// connect to canvas
var Module = {
print: (function() {
var element = document.getElementById('output');
element.value = ''; // clear browser cache
return function(text) {
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
element.value += text + "\n";
element.scrollTop = 99999; // focus on bottom
};
})(),
printErr: function(text) {
if (0) { // XXX disabled for safety typeof dump == 'function') {
dump(text + '\n'); // fast, straight to the real console
} else {
console.log(text);
}
},
canvas: document.getElementById('canvas'),
setStatus: function(text) {
if (Module.setStatus.interval) clearInterval(Module.setStatus.interval);
document.getElementById('status').innerHTML = text;
if (text) {
var counter = 0;
Module.setStatus.interval = setInterval(function() {
counter++;
counter %= 3;
var dots = ' ';
for (var i = 0; i < counter; i++) dots += '.';
dots += '*';
for (var i = counter; i < 2; i++) dots += '.';
document.getElementById('status').innerHTML = text.replace('...', dots);
}, 300);
}
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Downloading: ' + (this.totalDependencies-left) + '/' + this.totalDependencies + '...' : 'All downloads complete.');
}
};
Module.setStatus('Downloading...');
</script>
<script type='text/javascript'>
// Note: Some Emscripten settings will significantly limit the speed of the generated code.
// Note: Some Emscripten settings may limit the speed of the generated code.
// TODO: " u s e s t r i c t ";
try {
this['Module'] = Module;
} catch(e) {
this['Module'] = Module = {};
}
// The environment setup code below is customized to use Module.
// *** Environment setup code ***
var ENVIRONMENT_IS_NODE = typeof process === 'object';
var ENVIRONMENT_IS_WEB = typeof window === 'object';
var ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
if (ENVIRONMENT_IS_NODE) {
// Expose functionality in the same simple way that the shells work
// Note that we pollute the global namespace here, otherwise we break in node
Module['print'] = function(x) {
process['stdout'].write(x + '\n');
};
Module['printErr'] = function(x) {
process['stderr'].write(x + '\n');
};
var nodeFS = require('fs');
var nodePath = require('path');
Module['read'] = function(filename) {
filename = nodePath['normalize'](filename);
var ret = nodeFS['readFileSync'](filename).toString();
// The path is absolute if the normalized version is the same as the resolved.
if (!ret && filename != nodePath['resolve'](filename)) {
filename = path.join(__dirname, '..', 'src', filename);
ret = nodeFS['readFileSync'](filename).toString();
}
return ret;
};
Module['load'] = function(f) {
globalEval(read(f));
};
if (!Module['arguments']) {
Module['arguments'] = process['argv'].slice(2);
}
} else if (ENVIRONMENT_IS_SHELL) {
Module['print'] = print;
Module['printErr'] = printErr;
// Polyfill over SpiderMonkey/V8 differences
if (typeof read != 'undefined') {
Module['read'] = read;
} else {
Module['read'] = function(f) { snarf(f) };
}
if (!Module['arguments']) {
if (typeof scriptArgs != 'undefined') {
Module['arguments'] = scriptArgs;
} else if (typeof arguments != 'undefined') {
Module['arguments'] = arguments;
}
}
} else if (ENVIRONMENT_IS_WEB) {
if (!Module['print']) {
Module['print'] = function(x) {
console.log(x);
};
}
if (!Module['printErr']) {
Module['printErr'] = function(x) {
console.log(x);
};
}
Module['read'] = function(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send(null);
return xhr.responseText;
};
if (!Module['arguments']) {
if (typeof arguments != 'undefined') {
Module['arguments'] = arguments;
}
}
} else if (ENVIRONMENT_IS_WORKER) {
// We can do very little here...
Module['load'] = importScripts;
} else {
throw 'Unknown runtime environment. Where are we?';
}
function globalEval(x) {
eval.call(null, x);
}
if (!Module['load'] == 'undefined' && Module['read']) {
Module['load'] = function(f) {
globalEval(Module['read'](f));
};
}
if (!Module['printErr']) {
Module['printErr'] = function(){};
}
if (!Module['print']) {
Module['print'] = Module['printErr'];
}
if (!Module['arguments']) {
Module['arguments'] = [];
}
// *** Environment setup code ***
// Closure helpers
Module.print = Module['print'];
Module.printErr = Module['printErr'];
// === Auto-generated preamble library stuff ===
//========================================
// Runtime code shared with compiler
//========================================
var Runtime = {
stackSave: function () {
return STACKTOP;
},
stackRestore: function (stackTop) {
STACKTOP = stackTop;
},
forceAlign: function (target, quantum) {
quantum = quantum || 4;
if (quantum == 1) return target;
if (isNumber(target) && isNumber(quantum)) {
return Math.ceil(target/quantum)*quantum;
} else if (isNumber(quantum) && isPowerOfTwo(quantum)) {
var logg = log2(quantum);
return '((((' +target + ')+' + (quantum-1) + ')>>' + logg + ')<<' + logg + ')';
}
return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum;
},
isNumberType: function (type) {
return type in Runtime.INT_TYPES || type in Runtime.FLOAT_TYPES;
},
isPointerType: function isPointerType(type) {
return type[type.length-1] == '*';
},
isStructType: function isStructType(type) {
if (isPointerType(type)) return false;
if (/^\[\d+\ x\ (.*)\]/.test(type)) return true; // [15 x ?] blocks. Like structs
if (/<?{ ?[^}]* ?}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types
// See comment in isStructPointerType()
return type[0] == '%';
},
INT_TYPES: {"i1":0,"i8":0,"i16":0,"i32":0,"i64":0},
FLOAT_TYPES: {"float":0,"double":0},
bitshift64: function (low, high, op, bits) {
var ander = Math.pow(2, bits)-1;
if (bits < 32) {
switch (op) {
case 'shl':
return [low << bits, (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits))];
case 'ashr':
return [(((low >>> bits ) | ((high&ander) << (32 - bits))) >> 0) >>> 0, (high >> bits) >>> 0];
case 'lshr':
return [((low >>> bits) | ((high&ander) << (32 - bits))) >>> 0, high >>> bits];
}
} else if (bits == 32) {
switch (op) {
case 'shl':
return [0, low];
case 'ashr':
return [high, (high|0) < 0 ? ander : 0];
case 'lshr':
return [high, 0];
}
} else { // bits > 32
switch (op) {
case 'shl':
return [0, low << (bits - 32)];
case 'ashr':
return [(high >> (bits - 32)) >>> 0, (high|0) < 0 ? ander : 0];
case 'lshr':
return [high >>> (bits - 32) , 0];
}
}
abort('unknown bitshift64 op: ' + [value, op, bits]);
},
or64: function (x, y) {
var l = (x | 0) | (y | 0);
var h = (Math.round(x / 4294967296) | Math.round(y / 4294967296)) * 4294967296;
return l + h;
},
and64: function (x, y) {
var l = (x | 0) & (y | 0);
var h = (Math.round(x / 4294967296) & Math.round(y / 4294967296)) * 4294967296;
return l + h;
},
xor64: function (x, y) {
var l = (x | 0) ^ (y | 0);
var h = (Math.round(x / 4294967296) ^ Math.round(y / 4294967296)) * 4294967296;
return l + h;
},
getNativeTypeSize: function (type, quantumSize) {
if (Runtime.QUANTUM_SIZE == 1) return 1;
var size = {
'%i1': 1,
'%i8': 1,
'%i16': 2,
'%i32': 4,
'%i64': 8,
"%float": 4,
"%double": 8
}['%'+type]; // add '%' since float and double confuse Closure compiler as keys, and also spidermonkey as a compiler will remove 's from '_i8' etc
if (!size) {
if (type[type.length-1] == '*') {
size = Runtime.QUANTUM_SIZE; // A pointer
} else if (type[0] == 'i') {
var bits = parseInt(type.substr(1));
assert(bits % 8 == 0);
size = bits/8;
}
}
return size;
},
getNativeFieldSize: function (type) {
return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE);
},
dedup: function dedup(items, ident) {
var seen = {};
if (ident) {
return items.filter(function(item) {
if (seen[item[ident]]) return false;
seen[item[ident]] = true;
return true;
});
} else {
return items.filter(function(item) {
if (seen[item]) return false;
seen[item] = true;
return true;
});
}
},
set: function set() {
var args = typeof arguments[0] === 'object' ? arguments[0] : arguments;
var ret = {};
for (var i = 0; i < args.length; i++) {
ret[args[i]] = 0;
}
return ret;
},
calculateStructAlignment: function calculateStructAlignment(type) {
type.flatSize = 0;
type.alignSize = 0;
var diffs = [];
var prev = -1;
type.flatIndexes = type.fields.map(function(field) {
var size, alignSize;
if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) {
size = Runtime.getNativeTypeSize(field); // pack char; char; in structs, also char[X]s.
alignSize = size;
} else if (Runtime.isStructType(field)) {
size = Types.types[field].flatSize;
alignSize = Types.types[field].alignSize;
} else {
throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]);
}
alignSize = type.packed ? 1 : Math.min(alignSize, Runtime.QUANTUM_SIZE);
type.alignSize = Math.max(type.alignSize, alignSize);
var curr = Runtime.alignMemory(type.flatSize, alignSize); // if necessary, place this on aligned memory
type.flatSize = curr + size;
if (prev >= 0) {
diffs.push(curr-prev);
}
prev = curr;
return curr;
});
type.flatSize = Runtime.alignMemory(type.flatSize, type.alignSize);
if (diffs.length == 0) {
type.flatFactor = type.flatSize;
} else if (Runtime.dedup(diffs).length == 1) {
type.flatFactor = diffs[0];
}
type.needsFlattening = (type.flatFactor != 1);
return type.flatIndexes;
},
generateStructInfo: function (struct, typeName, offset) {
var type, alignment;
if (typeName) {
offset = offset || 0;
type = (typeof Types === 'undefined' ? Runtime.typeInfo : Types.types)[typeName];
if (!type) return null;
assert(type.fields.length === struct.length, 'Number of named fields must match the type for ' + typeName);
alignment = type.flatIndexes;
} else {
var type = { fields: struct.map(function(item) { return item[0] }) };
alignment = Runtime.calculateStructAlignment(type);
}
var ret = {
__size__: type.flatSize
};
if (typeName) {
struct.forEach(function(item, i) {
if (typeof item === 'string') {
ret[item] = alignment[i] + offset;
} else {
// embedded struct
var key;
for (var k in item) key = k;
ret[key] = Runtime.generateStructInfo(item[key], type.fields[i], alignment[i]);
}
});
} else {
struct.forEach(function(item, i) {
ret[item[1]] = alignment[i];
});
}
return ret;
},
addFunction: function (func) {
var ret = FUNCTION_TABLE.length;
FUNCTION_TABLE.push(func);
FUNCTION_TABLE.push(0);
return ret;
},
warnOnce: function (text) {
if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};
if (!Runtime.warnOnce.shown[text]) {
Runtime.warnOnce.shown[text] = 1;
Module.printErr(text);
}
},
funcWrappers: {},
getFuncWrapper: function (func) {
if (!Runtime.funcWrappers[func]) {
Runtime.funcWrappers[func] = function() {
FUNCTION_TABLE[func].apply(null, arguments);
};
}
return Runtime.funcWrappers[func];
},
stackAlloc: function stackAlloc(size) { var ret = STACKTOP;STACKTOP += size;STACKTOP = ((((STACKTOP)+3)>>2)<<2);assert(STACKTOP < STACK_ROOT + STACK_MAX, "Ran out of stack"); return ret; },
staticAlloc: function staticAlloc(size) { var ret = STATICTOP;STATICTOP += size;STATICTOP = ((((STATICTOP)+3)>>2)<<2); if (STATICTOP >= TOTAL_MEMORY) enlargeMemory();; return ret; },
alignMemory: function alignMemory(size,quantum) { var ret = size = Math.ceil((size)/(quantum ? quantum : 4))*(quantum ? quantum : 4); return ret; },
makeBigInt: function makeBigInt(low,high,unsigned) { var ret = (unsigned ? (((low)>>>0)+(((high)>>>0)*4294967296)) : (((low)>>>0)+(((high)|0)*4294967296))); return ret; },
QUANTUM_SIZE: 4,
__dummy__: 0
}
var CorrectionsMonitor = {
MAX_ALLOWED: 0, // XXX
corrections: 0,
sigs: {},
note: function(type, succeed, sig) {
if (!succeed) {
this.corrections++;
if (this.corrections >= this.MAX_ALLOWED) abort('\n\nToo many corrections!');
}
},
print: function() {
}
};
//========================================
// Runtime essentials
//========================================
var __THREW__ = false; // Used in checking for thrown exceptions.
var ABORT = false;
var undef = 0;
// tempInt is used for 32-bit signed values or smaller. tempBigInt is used
// for 32-bit unsigned values or more than 32 bits. TODO: audit all uses of tempInt
var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD;
var tempI64, tempI64b;
function abort(text) {
Module.print(text + ':\n' + (new Error).stack);
ABORT = true;
throw "Assertion: " + text;
}
function assert(condition, text) {
if (!condition) {
abort('Assertion failed: ' + text);
}
}
var globalScope = this;
// C calling interface. A convenient way to call C functions (in C files, or
// defined with extern "C").
//
// Note: LLVM optimizations can inline and remove functions, after which you will not be
// able to call them. Adding
//
// __attribute__((used))
//
// to the function definition will prevent that.
//
// Note: Closure optimizations will minify function names, making
// functions no longer callable. If you run closure (on by default
// in -O2 and above), you should export the functions you will call
// by calling emcc with something like
//
// -s EXPORTED_FUNCTIONS='["_func1","_func2"]'
//
// @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C")
// @param returnType The return type of the function, one of the JS types 'number', 'string' or 'array' (use 'number' for any C pointer, and
// 'array' for JavaScript arrays and typed arrays).
// @param argTypes An array of the types of arguments for the function (if there are no arguments, this can be ommitted). Types are as in returnType,
// except that 'array' is not possible (there is no way for us to know the length of the array)
// @param args An array of the arguments to the function, as native JS values (as in returnType)
// Note that string arguments will be stored on the stack (the JS string will become a C string on the stack).
// @return The return value, as a native JS value (as in returnType)
function ccall(ident, returnType, argTypes, args) {
var stack = 0;
function toC(value, type) {
if (type == 'string') {
if (!stack) stack = Runtime.stackSave();
var ret = Runtime.stackAlloc(value.length+1);
writeStringToMemory(value, ret);
return ret;
} else if (type == 'array') {
if (!stack) stack = Runtime.stackSave();
var ret = Runtime.stackAlloc(value.length);
writeArrayToMemory(value, ret);
return ret;
}
return value;
}
function fromC(value, type) {
if (type == 'string') {
return Pointer_stringify(value);
}
assert(type != 'array');
return value;
}
try {
var func = eval('_' + ident);
} catch(e) {
try {
func = globalScope['Module']['_' + ident]; // closure exported function
} catch(e) {}
}
assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)');
var i = 0;
var cArgs = args ? args.map(function(arg) {
return toC(arg, argTypes[i++]);
}) : [];
var ret = fromC(func.apply(null, cArgs), returnType);
if (stack) Runtime.stackRestore(stack);
return ret;
}
Module["ccall"] = ccall;
// Returns a native JS wrapper for a C function. This is similar to ccall, but
// returns a function you can call repeatedly in a normal way. For example:
//
// var my_function = cwrap('my_c_function', 'number', ['number', 'number']);
// alert(my_function(5, 22));
// alert(my_function(99, 12));
//
function cwrap(ident, returnType, argTypes) {
// TODO: optimize this, eval the whole function once instead of going through ccall each time
return function() {
return ccall(ident, returnType, argTypes, Array.prototype.slice.call(arguments));
}
}
Module["cwrap"] = cwrap;
// Sets a value in memory in a dynamic way at run-time. Uses the
// type data. This is the same as makeSetValue, except that
// makeSetValue is done at compile-time and generates the needed
// code then, whereas this function picks the right code at
// run-time.
// Note that setValue and getValue only do *aligned* writes and reads!
// Note that ccall uses JS types as for defining types, while setValue and
// getValue need LLVM types ('i8', 'i32') - this is a lower-level operation
function setValue(ptr, value, type, noSafe) {
type = type || 'i8';
if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit
switch(type) {
case 'i1': HEAP8[(ptr)]=value; break;
case 'i8': HEAP8[(ptr)]=value; break;
case 'i16': HEAP16[((ptr)>>1)]=value; break;
case 'i32': HEAP32[((ptr)>>2)]=value; break;
case 'i64': HEAP32[((ptr)>>2)]=value; break;
case 'float': HEAPF32[((ptr)>>2)]=value; break;
case 'double': (tempDoubleF64[0]=value,HEAP32[((ptr)>>2)]=tempDoubleI32[0],HEAP32[(((ptr)+(4))>>2)]=tempDoubleI32[1]); break;
default: abort('invalid type for setValue: ' + type);
}
}
Module['setValue'] = setValue;
// Parallel to setValue.
function getValue(ptr, type, noSafe) {
type = type || 'i8';
if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit
switch(type) {
case 'i1': return HEAP8[(ptr)];
case 'i8': return HEAP8[(ptr)];
case 'i16': return HEAP16[((ptr)>>1)];
case 'i32': return HEAP32[((ptr)>>2)];
case 'i64': return HEAP32[((ptr)>>2)];
case 'float': return HEAPF32[((ptr)>>2)];
case 'double': return (tempDoubleI32[0]=HEAP32[((ptr)>>2)],tempDoubleI32[1]=HEAP32[(((ptr)+(4))>>2)],tempDoubleF64[0]);
default: abort('invalid type for setValue: ' + type);
}
return null;
}
Module['getValue'] = getValue;
// Allocates memory for some data and initializes it properly.
var ALLOC_NORMAL = 0; // Tries to use _malloc()
var ALLOC_STACK = 1; // Lives for the duration of the current function call
var ALLOC_STATIC = 2; // Cannot be freed
Module['ALLOC_NORMAL'] = ALLOC_NORMAL;
Module['ALLOC_STACK'] = ALLOC_STACK;
Module['ALLOC_STATIC'] = ALLOC_STATIC;
function allocate(slab, types, allocator) {
var zeroinit, size;
if (typeof slab === 'number') {
zeroinit = true;
size = slab;
} else {
zeroinit = false;
size = slab.length;
}
var singleType = typeof types === 'string' ? types : null;
var ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length));
if (zeroinit) {
_memset(ret, 0, size);
return ret;
}
var i = 0, type;
while (i < size) {
var curr = slab[i];
if (typeof curr === 'function') {
curr = Runtime.getFunctionIndex(curr);
}
type = singleType || types[i];
if (type === 0) {
i++;
continue;
}
assert(type, 'Must know what type to store in allocate!');
if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later
setValue(ret+i, curr, type);
i += Runtime.getNativeTypeSize(type);
}
return ret;
}
Module['allocate'] = allocate;
function Pointer_stringify(ptr, /* optional */ length) {
var nullTerminated = typeof(length) == "undefined";
var ret = "";
var i = 0;
var t;
var nullByte = String.fromCharCode(0);
while (1) {
t = String.fromCharCode(HEAPU8[((ptr)+(i))]);
if (nullTerminated && t == nullByte) { break; } else {}
ret += t;
i += 1;
if (!nullTerminated && i == length) { break; }
}
return ret;
}
Module['Pointer_stringify'] = Pointer_stringify;
function Array_stringify(array) {
var ret = "";
for (var i = 0; i < array.length; i++) {
ret += String.fromCharCode(array[i]);
}
return ret;
}
Module['Array_stringify'] = Array_stringify;
// Memory management
var FUNCTION_TABLE; // XXX: In theory the indexes here can be equal to pointers to stacked or malloced memory. Such comparisons should
// be false, but can turn out true. We should probably set the top bit to prevent such issues.
var PAGE_SIZE = 4096;
function alignMemoryPage(x) {
return ((x+4095)>>12)<<12;
}
var HEAP;
var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;
var STACK_ROOT, STACKTOP, STACK_MAX;
var STATICTOP;
function enlargeMemory() {
// TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top.
Module.printErr('Warning: Enlarging memory arrays, this is not fast! ' + [STATICTOP, TOTAL_MEMORY]);
assert(STATICTOP >= TOTAL_MEMORY);
assert(TOTAL_MEMORY > 4); // So the loop below will not be infinite
while (TOTAL_MEMORY <= STATICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it
TOTAL_MEMORY = alignMemoryPage(2*TOTAL_MEMORY);
}
var oldHEAP8 = HEAP8;
var buffer = new ArrayBuffer(TOTAL_MEMORY);
HEAP8 = new Int8Array(buffer);
HEAP16 = new Int16Array(buffer);
HEAP32 = new Int32Array(buffer);
HEAPU8 = new Uint8Array(buffer);
HEAPU16 = new Uint16Array(buffer);
HEAPU32 = new Uint32Array(buffer);
HEAPF32 = new Float32Array(buffer);
HEAPF64 = new Float64Array(buffer);
HEAP8.set(oldHEAP8);
}
var TOTAL_STACK = Module['TOTAL_STACK'] || 5242880;
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 10485760;
var FAST_MEMORY = Module['FAST_MEMORY'] || 2097152;
// Initialize the runtime's memory
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
'Cannot fallback to non-typed array case: Code is too specialized');
var buffer = new ArrayBuffer(TOTAL_MEMORY);
HEAP8 = new Int8Array(buffer);
HEAP16 = new Int16Array(buffer);
HEAP32 = new Int32Array(buffer);
HEAPU8 = new Uint8Array(buffer);
HEAPU16 = new Uint16Array(buffer);
HEAPU32 = new Uint32Array(buffer);
HEAPF32 = new Float32Array(buffer);
HEAPF64 = new Float64Array(buffer);
// Endianness check (note: assumes compiler arch was little-endian)
HEAP32[0] = 255;
assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system');
var base = intArrayFromString('(null)'); // So printing %s of NULL gives '(null)'
// Also this ensures we leave 0 as an invalid address, 'NULL'
STATICTOP = base.length;
for (var i = 0; i < base.length; i++) {
HEAP8[(i)]=base[i]
}
Module['HEAP'] = HEAP;
Module['HEAP8'] = HEAP8;
Module['HEAP16'] = HEAP16;
Module['HEAP32'] = HEAP32;
Module['HEAPU8'] = HEAPU8;
Module['HEAPU16'] = HEAPU16;
Module['HEAPU32'] = HEAPU32;
Module['HEAPF32'] = HEAPF32;
Module['HEAPF64'] = HEAPF64;
STACK_ROOT = STACKTOP = Runtime.alignMemory(STATICTOP);
STACK_MAX = STACK_ROOT + TOTAL_STACK;
var tempDoublePtr = Runtime.alignMemory(STACK_MAX, 8);
var tempDoubleI8 = HEAP8.subarray(tempDoublePtr);
var tempDoubleI32 = HEAP32.subarray(tempDoublePtr >> 2);
var tempDoubleF32 = HEAPF32.subarray(tempDoublePtr >> 2);
var tempDoubleF64 = HEAPF64.subarray(tempDoublePtr >> 3);
function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much
tempDoubleI8[0] = HEAP8[ptr];
tempDoubleI8[1] = HEAP8[ptr+1];
tempDoubleI8[2] = HEAP8[ptr+2];
tempDoubleI8[3] = HEAP8[ptr+3];
}
function copyTempDouble(ptr) {
tempDoubleI8[0] = HEAP8[ptr];
tempDoubleI8[1] = HEAP8[ptr+1];
tempDoubleI8[2] = HEAP8[ptr+2];
tempDoubleI8[3] = HEAP8[ptr+3];
tempDoubleI8[4] = HEAP8[ptr+4];
tempDoubleI8[5] = HEAP8[ptr+5];
tempDoubleI8[6] = HEAP8[ptr+6];
tempDoubleI8[7] = HEAP8[ptr+7];
}
STACK_MAX = tempDoublePtr + 8;
STATICTOP = alignMemoryPage(STACK_MAX);
function callRuntimeCallbacks(callbacks) {
while(callbacks.length > 0) {
var callback = callbacks.shift();
var func = callback.func;
if (typeof func === 'number') {
func = FUNCTION_TABLE[func];
}
func(callback.arg === undefined ? null : callback.arg);
}
}
var __ATINIT__ = []; // functions called during startup
var __ATMAIN__ = []; // functions called when main() is to be run
var __ATEXIT__ = []; // functions called during shutdown
function initRuntime() {
callRuntimeCallbacks(__ATINIT__);
}
function preMain() {
callRuntimeCallbacks(__ATMAIN__);
}
function exitRuntime() {
callRuntimeCallbacks(__ATEXIT__);
// Print summary of correction activity
CorrectionsMonitor.print();
}
function String_len(ptr) {
var i = 0;
while (HEAP8[((ptr)+(i))]) i++; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds
return i;
}
Module['String_len'] = String_len;
// Tools
// This processes a JS string into a C-line array of numbers, 0-terminated.
// For LLVM-originating strings, see parser.js:parseLLVMString function
function intArrayFromString(stringy, dontAddNull, length /* optional */) {
var ret = [];
var t;
var i = 0;
if (length === undefined) {
length = stringy.length;
}
while (i < length) {
var chr = stringy.charCodeAt(i);
if (chr > 0xFF) {
assert(false, 'Character code ' + chr + ' (' + stringy[i] + ') at offset ' + i + ' not in 0x00-0xFF.');
chr &= 0xFF;
}
ret.push(chr);
i = i + 1;
}
if (!dontAddNull) {
ret.push(0);
}
return ret;
}
Module['intArrayFromString'] = intArrayFromString;
function intArrayToString(array) {
var ret = [];
for (var i = 0; i < array.length; i++) {
var chr = array[i];
if (chr > 0xFF) {
assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.');
chr &= 0xFF;
}
ret.push(String.fromCharCode(chr));
}
return ret.join('');
}
Module['intArrayToString'] = intArrayToString;
// Write a Javascript array to somewhere in the heap
function writeStringToMemory(string, buffer, dontAddNull) {
var i = 0;
while (i < string.length) {
var chr = string.charCodeAt(i);
if (chr > 0xFF) {
assert(false, 'Character code ' + chr + ' (' + string[i] + ') at offset ' + i + ' not in 0x00-0xFF.');
chr &= 0xFF;
}
HEAP8[((buffer)+(i))]=chr
i = i + 1;
}
if (!dontAddNull) {
HEAP8[((buffer)+(i))]=0
}
}
Module['writeStringToMemory'] = writeStringToMemory;
function writeArrayToMemory(array, buffer) {
for (var i = 0; i < array.length; i++) {
HEAP8[((buffer)+(i))]=array[i];
}
}
Module['writeArrayToMemory'] = writeArrayToMemory;
var STRING_TABLE = [];
function unSign(value, bits, ignore, sig) {
if (value >= 0) {
return value;
}
return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts
: Math.pow(2, bits) + value;
// TODO: clean up previous line
}
function reSign(value, bits, ignore, sig) {
if (value <= 0) {
return value;
}
var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32
: Math.pow(2, bits-1);
if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that
// but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors
// TODO: In i64 mode 1, resign the two parts separately and safely
value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
}
return value;
}
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
// decrement it. Incrementing must happen in a place like
// PRE_RUN_ADDITIONS (used by emcc to add file preloading).
// Note that you can add dependencies in preRun, even though
// it happens right before run - run will be postponed until
// the dependencies are met.
var runDependencies = 0;
function addRunDependency() {
runDependencies++;
if (Module['monitorRunDependencies']) {
Module['monitorRunDependencies'](runDependencies);
}
}
function removeRunDependency() {
runDependencies--;
if (Module['monitorRunDependencies']) {
Module['monitorRunDependencies'](runDependencies);
}
if (runDependencies == 0) run();
}
// === Body ===
function _main() {
;
var __label__;
var $1;
var $surf;
$1=0;
var $2=_SDL_Init(32);
var $3=_SDL_SetVideoMode(320, 200, 32, 0);
$surf=$3;
var $4=$surf;
var $5=_circleRGBA($4, 160, 100, 100, -1, -1, -1, -1);
var $6=$surf;
var $7=_SDL_Flip($6);
_SDL_Quit();
;
return 0;
}
Module["_main"] = _main;
// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included
var i64Math = null;
var ERRNO_CODES={E2BIG:7,EACCES:13,EADDRINUSE:98,EADDRNOTAVAIL:99,EAFNOSUPPORT:97,EAGAIN:11,EALREADY:114,EBADF:9,EBADMSG:74,EBUSY:16,ECANCELED:125,ECHILD:10,ECONNABORTED:103,ECONNREFUSED:111,ECONNRESET:104,EDEADLK:35,EDESTADDRREQ:89,EDOM:33,EDQUOT:122,EEXIST:17,EFAULT:14,EFBIG:27,EHOSTUNREACH:113,EIDRM:43,EILSEQ:84,EINPROGRESS:115,EINTR:4,EINVAL:22,EIO:5,EISCONN:106,EISDIR:21,ELOOP:40,EMFILE:24,EMLINK:31,EMSGSIZE:90,EMULTIHOP:72,ENAMETOOLONG:36,ENETDOWN:100,ENETRESET:102,ENETUNREACH:101,ENFILE:23,ENOBUFS:105,ENODATA:61,ENODEV:19,ENOENT:2,ENOEXEC:8,ENOLCK:37,ENOLINK:67,ENOMEM:12,ENOMSG:42,ENOPROTOOPT:92,ENOSPC:28,ENOSR:63,ENOSTR:60,ENOSYS:38,ENOTCONN:107,ENOTDIR:20,ENOTEMPTY:39,ENOTRECOVERABLE:131,ENOTSOCK:88,ENOTSUP:95,ENOTTY:25,ENXIO:6,EOVERFLOW:75,EOWNERDEAD:130,EPERM:1,EPIPE:32,EPROTO:71,EPROTONOSUPPORT:93,EPROTOTYPE:91,ERANGE:34,EROFS:30,ESPIPE:29,ESRCH:3,ESTALE:116,ETIME:62,ETIMEDOUT:110,ETXTBSY:26,EWOULDBLOCK:11,EXDEV:18};
function ___setErrNo(value) {
// For convenient setting and returning of errno.
if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_STATIC);
HEAP32[((___setErrNo.ret)>>2)]=value
return value;
}
var _stdin=0;
var _stdout=0;
var _stderr=0;
var __impure_ptr=0;var FS={currentPath:"/",nextInode:2,streams:[null],ignorePermissions:true,absolutePath:function (relative, base) {
if (typeof relative !== 'string') return null;
if (base === undefined) base = FS.currentPath;
if (relative && relative[0] == '/') base = '';
var full = base + '/' + relative;
var parts = full.split('/').reverse();
var absolute = [''];
while (parts.length) {
var part = parts.pop();
if (part == '' || part == '.') {
// Nothing.
} else if (part == '..') {
if (absolute.length > 1) absolute.pop();
} else {
absolute.push(part);
}
}
return absolute.length == 1 ? '/' : absolute.join('/');
},analyzePath:function (path, dontResolveLastLink, linksVisited) {
var ret = {
isRoot: false,
exists: false,
error: 0,
name: null,
path: null,
object: null,
parentExists: false,
parentPath: null,
parentObject: null
};
path = FS.absolutePath(path);
if (path == '/') {
ret.isRoot = true;
ret.exists = ret.parentExists = true;
ret.name = '/';
ret.path = ret.parentPath = '/';
ret.object = ret.parentObject = FS.root;
} else if (path !== null) {
linksVisited = linksVisited || 0;
path = path.slice(1).split('/');
var current = FS.root;
var traversed = [''];
while (path.length) {
if (path.length == 1 && current.isFolder) {
ret.parentExists = true;
ret.parentPath = traversed.length == 1 ? '/' : traversed.join('/');
ret.parentObject = current;
ret.name = path[0];
}
var target = path.shift();
if (!current.isFolder) {
ret.error = ERRNO_CODES.ENOTDIR;
break;
} else if (!current.read) {
ret.error = ERRNO_CODES.EACCES;
break;
} else if (!current.contents.hasOwnProperty(target)) {
ret.error = ERRNO_CODES.ENOENT;
break;
}
current = current.contents[target];
if (current.link && !(dontResolveLastLink && path.length == 0)) {
if (linksVisited > 40) { // Usual Linux SYMLOOP_MAX.
ret.error = ERRNO_CODES.ELOOP;
break;
}
var link = FS.absolutePath(current.link, traversed.join('/'));
ret = FS.analyzePath([link].concat(path).join('/'),
dontResolveLastLink, linksVisited + 1);
return ret;
}
traversed.push(target);
if (path.length == 0) {
ret.exists = true;
ret.path = traversed.join('/');
ret.object = current;
}
}
}
return ret;
},findObject:function (path, dontResolveLastLink) {
FS.ensureRoot();
var ret = FS.analyzePath(path, dontResolveLastLink);
if (ret.exists) {
return ret.object;
} else {
___setErrNo(ret.error);
return null;
}
},createObject:function (parent, name, properties, canRead, canWrite) {
if (!parent) parent = '/';
if (typeof parent === 'string') parent = FS.findObject(parent);
if (!parent) {
___setErrNo(ERRNO_CODES.EACCES);
throw new Error('Parent path must exist.');
}
if (!parent.isFolder) {
___setErrNo(ERRNO_CODES.ENOTDIR);
throw new Error('Parent must be a folder.');
}
if (!parent.write && !FS.ignorePermissions) {
___setErrNo(ERRNO_CODES.EACCES);
throw new Error('Parent folder must be writeable.');
}
if (!name || name == '.' || name == '..') {
___setErrNo(ERRNO_CODES.ENOENT);
throw new Error('Name must not be empty.');
}
if (parent.contents.hasOwnProperty(name)) {
___setErrNo(ERRNO_CODES.EEXIST);
throw new Error("Can't overwrite object.");
}
parent.contents[name] = {
read: canRead === undefined ? true : canRead,
write: canWrite === undefined ? false : canWrite,
timestamp: Date.now(),
inodeNumber: FS.nextInode++
};
for (var key in properties) {
if (properties.hasOwnProperty(key)) {
parent.contents[name][key] = properties[key];
}
}
return parent.contents[name];
},createFolder:function (parent, name, canRead, canWrite) {
var properties = {isFolder: true, isDevice: false, contents: {}};
return FS.createObject(parent, name, properties, canRead, canWrite);
},createPath:function (parent, path, canRead, canWrite) {
var current = FS.findObject(parent);
if (current === null) throw new Error('Invalid parent.');
path = path.split('/').reverse();
while (path.length) {
var part = path.pop();
if (!part) continue;
if (!current.contents.hasOwnProperty(part)) {
FS.createFolder(current, part, canRead, canWrite);
}
current = current.contents[part];
}
return current;
},createFile:function (parent, name, properties, canRead, canWrite) {
properties.isFolder = false;
return FS.createObject(parent, name, properties, canRead, canWrite);
},createDataFile:function (parent, name, data, canRead, canWrite) {
if (typeof data === 'string') {
var dataArray = new Array(data.length);
for (var i = 0, len = data.length; i < len; ++i) dataArray[i] = data.charCodeAt(i);
data = dataArray;
}
var properties = {isDevice: false, contents: data};
return FS.createFile(parent, name, properties, canRead, canWrite);
},createLazyFile:function (parent, name, url, canRead, canWrite) {
var properties = {isDevice: false, url: url};
return FS.createFile(parent, name, properties, canRead, canWrite);
},createPreloadedFile:function (parent, name, url, canRead, canWrite) {
Browser.asyncLoad(url, function(data) {
FS.createDataFile(parent, name, data, canRead, canWrite);
});
},createLink:function (parent, name, target, canRead, canWrite) {
var properties = {isDevice: false, link: target};
return FS.createFile(parent, name, properties, canRead, canWrite);
},createDevice:function (parent, name, input, output) {
if (!(input || output)) {
throw new Error('A device must have at least one callback defined.');
}
var ops = {isDevice: true, input: input, output: output};
return FS.createFile(parent, name, ops, Boolean(input), Boolean(output));
},forceLoadFile:function (obj) {
if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
var success = true;
if (typeof XMLHttpRequest !== 'undefined') {
// Browser.
assert('Cannot do synchronous binary XHRs in modern browsers. Use --embed-file or --preload-file in emcc');
} else if (Module['read']) {
// Command-line.
try {
// WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
// read() will try to parse UTF8.
obj.contents = intArrayFromString(Module['read'](obj.url), true);
} catch (e) {
success = false;
}
} else {
throw new Error('Cannot load without read() or XMLHttpRequest.');
}
if (!success) ___setErrNo(ERRNO_CODES.EIO);
return success;
},ensureRoot:function () {
if (FS.root) return;
// The main file system tree. All the contents are inside this.
FS.root = {
read: true,
write: true,
isFolder: true,
isDevice: false,
timestamp: Date.now(),
inodeNumber: 1,
contents: {}
};
},init:function (input, output, error) {
// Make sure we initialize only once.
assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
FS.init.initialized = true;
FS.ensureRoot();
// Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
input = input || Module['stdin'];
output = output || Module['stdout'];
error = error || Module['stderr'];
// Default handlers.
var stdinOverridden = true, stdoutOverridden = true, stderrOverridden = true;
if (!input) {
stdinOverridden = false;
input = function() {
if (!input.cache || !input.cache.length) {
var result;
if (typeof window != 'undefined' &&
typeof window.prompt == 'function') {
// Browser.
result = window.prompt('Input: ');
if (result === null) result = String.fromCharCode(0); // cancel ==> EOF
} else if (typeof readline == 'function') {
// Command line.
result = readline();
}
if (!result) result = '';
input.cache = intArrayFromString(result + '\n', true);
}
return input.cache.shift();
};
}
function simpleOutput(val) {
if (val === null || val === '\n'.charCodeAt(0)) {
output.printer(output.buffer.join(''));
output.buffer = [];
} else {
output.buffer.push(String.fromCharCode(val));
}
}
if (!output) {
stdoutOverridden = false;
output = simpleOutput;
}
if (!output.printer) output.printer = Module['print'];
if (!output.buffer) output.buffer = [];
if (!error) {
stderrOverridden = false;
error = simpleOutput;
}
if (!error.printer) error.printer = Module['print'];
if (!error.buffer) error.buffer = [];
// Create the temporary folder, if not already created
try {
FS.createFolder('/', 'tmp', true, true);
} catch(e) {}
// Create the I/O devices.
var devFolder = FS.createFolder('/', 'dev', true, true);
var stdin = FS.createDevice(devFolder, 'stdin', input);
var stdout = FS.createDevice(devFolder, 'stdout', null, output);
var stderr = FS.createDevice(devFolder, 'stderr', null, error);
FS.createDevice(devFolder, 'tty', input, output);
// Create default streams.
FS.streams[1] = {
path: '/dev/stdin',
object: stdin,
position: 0,
isRead: true,
isWrite: false,
isAppend: false,
isTerminal: !stdinOverridden,
error: false,
eof: false,
ungotten: []
};
FS.streams[2] = {
path: '/dev/stdout',
object: stdout,
position: 0,
isRead: false,
isWrite: true,
isAppend: false,
isTerminal: !stdoutOverridden,
error: false,
eof: false,
ungotten: []
};
FS.streams[3] = {
path: '/dev/stderr',
object: stderr,
position: 0,
isRead: false,
isWrite: true,
isAppend: false,
isTerminal: !stderrOverridden,
error: false,
eof: false,
ungotten: []
};
_stdin = allocate([1], 'void*', ALLOC_STATIC);
_stdout = allocate([2], 'void*', ALLOC_STATIC);
_stderr = allocate([3], 'void*', ALLOC_STATIC);
// Other system paths
FS.createPath('/', 'dev/shm/tmp', true, true); // temp files
// Newlib initialization
FS.streams[_stdin] = FS.streams[1];
FS.streams[_stdout] = FS.streams[2];
FS.streams[_stderr] = FS.streams[3];
__impure_ptr = allocate([ allocate(
[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0],
'void*', ALLOC_STATIC) ], 'void*', ALLOC_STATIC);
},quit:function () {
if (!FS.init.initialized) return;
// Flush any partially-printed lines in stdout and stderr. Careful, they may have been closed
if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output('\n'.charCodeAt(0));
if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output('\n'.charCodeAt(0));
},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];
}};
var Browser={mainLoop:{scheduler:null,shouldPause:false,paused:false},pointerLock:false,moduleContextCreatedCallbacks:[],createContext:function (canvas, useWebGL, setInModule) {
try {
var ctx = canvas.getContext(useWebGL ? 'experimental-webgl' : '2d');
if (!ctx) throw ':(';
} catch (e) {
Module.print('Could not create canvas - ' + e);
return null;
}
if (useWebGL) {
// Set the background of the WebGL canvas to black
canvas.style.backgroundColor = "black";
// Warn on context loss
canvas.addEventListener('webglcontextlost', function(event) {
alert('WebGL context lost. You will need to reload the page.');
}, false);
}
if (setInModule) {
Module.ctx = ctx;
Module.useWebGL = useWebGL;
Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });
}
return ctx;
},requestFullScreen:function () {
var canvas = Module.canvas;
function fullScreenChange() {
if (document['webkitFullScreenElement'] === canvas ||
document['mozFullScreenElement'] === canvas ||
document['fullScreenElement'] === canvas) {
canvas.requestPointerLock = canvas['requestPointerLock'] ||
canvas['mozRequestPointerLock'] ||
canvas['webkitRequestPointerLock'];
canvas.requestPointerLock();
}
}
document.addEventListener('fullscreenchange', fullScreenChange, false);
document.addEventListener('mozfullscreenchange', fullScreenChange, false);
document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
function pointerLockChange() {
Browser.pointerLock = document['pointerLockElement'] === canvas ||
document['mozPointerLockElement'] === canvas ||
document['webkitPointerLockElement'] === canvas;
}
document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
canvas.requestFullScreen = canvas['requestFullScreen'] ||
canvas['mozRequestFullScreen'] ||
(canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
canvas.requestFullScreen();
},requestAnimationFrame:function (func) {
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = window['requestAnimationFrame'] ||
window['mozRequestAnimationFrame'] ||
window['webkitRequestAnimationFrame'] ||
window['msRequestAnimationFrame'] ||
window['oRequestAnimationFrame'] ||
window['setTimeout'];
}
window.requestAnimationFrame(func);
},getMovementX:function (delta, event) {
if (!Browser.pointerLock) return delta;
return event['movementX'] ||
event['mozMovementX'] ||
event['webkitMovementX'] ||
0; // delta;
},getMovementY:function (delta, event) {
if (!Browser.pointerLock) return delta;
return event['movementY'] ||
event['mozMovementY'] ||
event['webkitMovementY'] ||
0; // delta;
},asyncLoad:function (url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
var arrayBuffer = xhr.response;
assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).');
callback(new Uint8Array(arrayBuffer));
removeRunDependency();
};
xhr.onerror = function(event) {
assert(arrayBuffer, 'Loading data file "' + url + '" failed.');
};
xhr.send(null);
addRunDependency();
}};var SDL={defaults:{width:320,height:200,copyOnLock:true},version:null,surfaces:{},events:[],audios:[null],fonts:[null],keyboardState:null,shiftKey:false,ctrlKey:false,altKey:false,startTime:null,mouseX:0,mouseY:0,buttonState:0,DOMButtons:[0,0,0],DOMEventToSDLEvent:{},keyCodes:{16:304,17:305,18:308,33:1099,34:1102,37:1104,38:1106,39:1103,40:1105,96:1112,97:1113,98:1114,99:1115,100:1116,101:1117,102:1118,103:1119,104:1120,105:1121,112:1082,113:1083,114:1084,115:1085,116:1086,117:1087,118:1088,119:1089,120:1090,121:1091,122:1092,123:1093,173:45,188:44,190:46,191:47,192:96},scanCodes:{9:43,13:40,27:41,32:44,44:54,46:55,47:56,48:39,49:30,50:31,51:32,52:33,53:34,54:35,55:36,56:37,57:38,92:49,97:4,98:5,99:6,100:7,101:8,102:9,103:10,104:11,105:12,106:13,107:14,108:15,109:16,110:17,111:18,112:19,113:20,114:21,115:22,116:23,117:24,118:25,119:26,120:27,121:28,122:29,305:224,308:226},structs:{Rect:{__size__:16,x:0,y:4,w:8,h:12},PixelFormat:{__size__:36,format:0,palette:4,BitsPerPixel:8,BytesPerPixel:9,padding1:10,padding2:11,Rmask:12,Gmask:16,Bmask:20,Amask:24,Rloss:28,Gloss:29,Bloss:30,Aloss:31,Rshift:32,Gshift:33,Bshift:34,Ashift:35},KeyboardEvent:{__size__:16,type:0,windowID:4,state:8,repeat:9,padding2:10,padding3:11,keysym:12},keysym:{__size__:16,scancode:0,sym:4,mod:8,unicode:12},MouseMotionEvent:{__size__:28,type:0,windowID:4,state:8,padding1:9,padding2:10,padding3:11,x:12,y:16,xrel:20,yrel:24},MouseButtonEvent:{__size__:20,type:0,windowID:4,button:8,state:9,padding1:10,padding2:11,x:12,y:16},AudioSpec:{__size__:24,freq:0,format:4,channels:6,silence:7,samples:8,size:12,callback:16,userdata:20},version:{__size__:3,major:0,minor:1,patch:2}},loadRect:function (rect) {
return {
x: HEAP32[((rect + SDL.structs.Rect.x)>>2)],
y: HEAP32[((rect + SDL.structs.Rect.y)>>2)],
w: HEAP32[((rect + SDL.structs.Rect.w)>>2)],
h: HEAP32[((rect + SDL.structs.Rect.h)>>2)]
};
},loadColorToCSSRGB:function (color) {
var rgba = HEAP32[((color)>>2)];
return 'rgb(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ')';
},loadColorToCSSRGBA:function (color) {
var rgba = HEAP32[((color)>>2)];
return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (((rgba >> 24)&255)/255) + ')';
},translateColorToCSSRGBA:function (rgba) {
return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
},translateRGBAToCSSRGBA:function (r, g, b, a) {
return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
},makeSurface:function (width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) {
flags = flags || 0;
var surf = _malloc(14*Runtime.QUANTUM_SIZE); // SDL_Surface has 14 fields of quantum size
var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time
var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE);
flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked
HEAP32[((surf+Runtime.QUANTUM_SIZE*0)>>2)]=flags // SDL_Surface.flags
HEAP32[((surf+Runtime.QUANTUM_SIZE*1)>>2)]=pixelFormat // SDL_Surface.format TODO
HEAP32[((surf+Runtime.QUANTUM_SIZE*2)>>2)]=width // SDL_Surface.w
HEAP32[((surf+Runtime.QUANTUM_SIZE*3)>>2)]=height // SDL_Surface.h
HEAP32[((surf+Runtime.QUANTUM_SIZE*4)>>2)]=width*4 // SDL_Surface.pitch, assuming RGBA for now,
// since that is what ImageData gives us in browsers
HEAP32[((surf+Runtime.QUANTUM_SIZE*5)>>2)]=buffer // SDL_Surface.pixels
HEAP32[((surf+Runtime.QUANTUM_SIZE*6)>>2)]=0 // SDL_Surface.offset
HEAP32[((pixelFormat + SDL.structs.PixelFormat.format)>>2)]=-2042224636 // SDL_PIXELFORMAT_RGBA8888
HEAP32[((pixelFormat + SDL.structs.PixelFormat.palette)>>2)]=0 // TODO
HEAP8[(pixelFormat + SDL.structs.PixelFormat.BitsPerPixel)]=32 // TODO
HEAP8[(pixelFormat + SDL.structs.PixelFormat.BytesPerPixel)]=4 // TODO
HEAP32[((pixelFormat + SDL.structs.PixelFormat.Rmask)>>2)]=rmask || 0x000000ff
HEAP32[((pixelFormat + SDL.structs.PixelFormat.Gmask)>>2)]=gmask || 0x0000ff00
HEAP32[((pixelFormat + SDL.structs.PixelFormat.Bmask)>>2)]=bmask || 0x00ff0000
HEAP32[((pixelFormat + SDL.structs.PixelFormat.Amask)>>2)]=amask || 0xff000000
// Decide if we want to use WebGL or not
var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL
var canvas;
if (!usePageCanvas) {
canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
} else {
canvas = Module['canvas'];
}
var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas);
SDL.surfaces[surf] = {
width: width,
height: height,
canvas: canvas,
ctx: ctx,
surf: surf,
buffer: buffer,
pixelFormat: pixelFormat,
alpha: 255,
flags: flags,
locked: 0,
usePageCanvas: usePageCanvas,
source: source
};
return surf;
},freeSurface:function (surf) {
_free(SDL.surfaces[surf].buffer);
_free(SDL.surfaces[surf].pixelFormat);
_free(surf);
SDL.surfaces[surf] = null;
},receiveEvent:function (event) {
switch(event.type) {
case 'mousemove':
// workaround for firefox bug 750111
event['movementX'] = event['mozMovementX'];
event['movementY'] = event['mozMovementY'];
// fall through
case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'DOMMouseScroll':
if (event.type == 'DOMMouseScroll') {
event = {
type: 'mousedown',
button: event.detail > 0 ? 4 : 3,
pageX: event.pageX,
pageY: event.pageY
};
} else if (event.type == 'mousedown') {
SDL.DOMButtons[event.button] = 1;
} else if (event.type == 'mouseup') {
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;
}
SDL.events.push(event);
if (SDL.events.length >= 10000) {
Module.printErr('SDL event queue full, dropping earliest event');
SDL.events.shift();
}
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;
case 'mouseout':
// Un-press all pressed mouse buttons, because we might miss the release outside of the canvas
for (var i = 0; i < 3; i++) {
if (SDL.DOMButtons[i]) {
SDL.events.push({
type: 'mouseup',
button: i,
pageX: event.pageX,
pageY: event.pageY
});
SDL.DOMButtons[i] = 0;
}
}
break;
}
return false;
},makeCEvent:function (event, ptr) {
if (typeof event === 'number') {
// This is a pointer to a native C event that was SDL_PushEvent'ed
_memcpy(ptr, event, SDL.structs.KeyboardEvent.__size__); // XXX
return;
}
switch(event.type) {
case 'keydown': case 'keyup': {
var down = event.type === 'keydown';
//Module.print('Received key event: ' + event.keyCode);
var key = event.keyCode;
if (key >= 65 && key <= 90) {
key += 32; // make lowercase for SDL
} else {
key = SDL.keyCodes[event.keyCode] || event.keyCode;
}
var scan;
if (key >= 1024) {
scan = key - 1024;
} else {
scan = SDL.scanCodes[key] || key;
}
HEAP32[(((ptr)+(SDL.structs.KeyboardEvent.type))>>2)]=SDL.DOMEventToSDLEvent[event.type]
//HEAP32[(((ptr)+(SDL.structs.KeyboardEvent.which))>>2)]=1
HEAP8[((ptr)+(SDL.structs.KeyboardEvent.state))]=down ? 1 : 0
HEAP8[((ptr)+(SDL.structs.KeyboardEvent.repeat))]=0 // TODO
HEAP32[(((ptr)+(SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode))>>2)]=scan
HEAP32[(((ptr)+(SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym))>>2)]=key
HEAP32[(((ptr)+(SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod))>>2)]=0
HEAP32[(((ptr)+(SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode))>>2)]=key
HEAP8[((SDL.keyboardState)+(SDL.keyCodes[event.keyCode] || event.keyCode))]=event.type == "keydown";
SDL.shiftKey = event.shiftKey;
SDL.ctrlKey = event.ctrlKey;
SDL.altKey = event.altKey;
break;
}
case 'mousedown': case 'mouseup':
if (event.type == 'mousedown') {
// SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3,
// and DOM buttons are 0-2, so this means that the below formula is
// correct.
SDL.buttonState |= 1 << event.button;
} else if (event.type == 'mouseup') {
SDL.buttonState = 0;
}
// fall through
case 'mousemove': {
var x = event.pageX - Module['canvas'].offsetLeft;
var y = event.pageY - Module['canvas'].offsetTop;
if (event.type != 'mousemove') {
var down = event.type === 'mousedown';
HEAP32[(((ptr)+(SDL.structs.MouseButtonEvent.type))>>2)]=SDL.DOMEventToSDLEvent[event.type];
HEAP8[((ptr)+(SDL.structs.MouseButtonEvent.button))]=event.button+1; // DOM buttons are 0-2, SDL 1-3
HEAP8[((ptr)+(SDL.structs.MouseButtonEvent.state))]=down ? 1 : 0;
HEAP32[(((ptr)+(SDL.structs.MouseButtonEvent.x))>>2)]=x;
HEAP32[(((ptr)+(SDL.structs.MouseButtonEvent.y))>>2)]=y;
} else {
HEAP32[(((ptr)+(SDL.structs.MouseMotionEvent.type))>>2)]=SDL.DOMEventToSDLEvent[event.type];
HEAP8[((ptr)+(SDL.structs.MouseMotionEvent.state))]=SDL.buttonState;
HEAP32[(((ptr)+(SDL.structs.MouseMotionEvent.x))>>2)]=x;
HEAP32[(((ptr)+(SDL.structs.MouseMotionEvent.y))>>2)]=y;
HEAP32[(((ptr)+(SDL.structs.MouseMotionEvent.xrel))>>2)]=Browser.getMovementX(x - SDL.mouseX, event);
HEAP32[(((ptr)+(SDL.structs.MouseMotionEvent.yrel))>>2)]=Browser.getMovementY(y - SDL.mouseY, event);
}
SDL.mouseX = x;
SDL.mouseY = y;
break;
}
default: throw 'Unhandled SDL event: ' + event.type;
}
},estimateTextWidth:function (fontData, text) {
var h = fontData.size;
var fontString = h + 'px sans-serif';
// TODO: use temp context, not screen's, to avoid affecting its performance?
var tempCtx = SDL.surfaces[SDL.screen].ctx;
tempCtx.save();
tempCtx.font = fontString;
var ret = tempCtx.measureText(text).width | 0;
tempCtx.restore();
return ret;
},allocateChannels:function (num) { // called from Mix_AllocateChannels and init
if (SDL.numChannels && SDL.numChannels >= num) return;
SDL.numChannels = num;
SDL.channels = [];
for (var i = 0; i < num; i++) {
SDL.channels[i] = {
audio: null,
volume: 1.0
};
}
},debugSurface:function (surfData) {
console.log('dumping surface ' + [surfData.surf, surfData.source, surfData.width, surfData.height]);
var image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
var data = image.data;
var num = Math.min(surfData.width, surfData.height);
for (var i = 0; i < num; i++) {
console.log(' diagonal ' + i + ':' + [data[i*surfData.width*4 + i*4 + 0], data[i*surfData.width*4 + i*4 + 1], data[i*surfData.width*4 + i*4 + 2], data[i*surfData.width*4 + i*4 + 3]]);
}
}};function _SDL_Init(what) {
SDL.startTime = Date.now();
['keydown', 'keyup'].forEach(function(event) {
addEventListener(event, SDL.receiveEvent, true);
});
SDL.keyboardState = _malloc(0x10000);
_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['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
return 0; // success
}
function _SDL_SetVideoMode(width, height, depth, flags) {
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mouseout'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
Module['canvas'].width = width;
Module['canvas'].height = height;
return SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
}
var _circleRGBA; // stub for _circleRGBA
function _SDL_Flip(surf) {
// We actually do this in Unlock, since the screen surface has as its canvas
// backing the page canvas element
}
function _SDL_Quit() {
for (var i = 0; i < SDL.audios; i++) {
SDL.audios[i].pause();
}
Module.print('SDL_Quit called (and ignored)');
}
function _memcpy(dest, src, num, align) {
assert(num % 1 === 0, 'memcpy given ' + num + ' bytes to copy. Problem with quantum=1 corrections perhaps?');
if (num >= 20 && src % 2 == dest % 2) {
// This is unaligned, but quite large, and potentially alignable, so work hard to get to aligned settings
if (src % 4 == dest % 4) {
var stop = src + num;
while (src % 4) { // no need to check for stop, since we have large num
HEAP8[dest++] = HEAP8[src++];
}
var src4 = src >> 2, dest4 = dest >> 2, stop4 = stop >> 2;
while (src4 < stop4) {
HEAP32[dest4++] = HEAP32[src4++];
}
src = src4 << 2;
dest = dest4 << 2;
while (src < stop) {
HEAP8[dest++] = HEAP8[src++];
}
} else {
var stop = src + num;
if (src % 2) { // no need to check for stop, since we have large num
HEAP8[dest++] = HEAP8[src++];
}
var src2 = src >> 1, dest2 = dest >> 1, stop2 = stop >> 1;
while (src2 < stop2) {
HEAP16[dest2++] = HEAP16[src2++];
}
src = src2 << 1;
dest = dest2 << 1;
if (src < stop) {
HEAP8[dest++] = HEAP8[src++];
}
}
} else {
while (num--) {
HEAP8[dest++] = HEAP8[src++];
}
}
}
function _memset(ptr, value, num, align) {
// TODO: make these settings, and in memcpy, {{'s
if (num >= 20) {
// This is unaligned, but quite large, so work hard to get to aligned settings
var stop = ptr + num;
while (ptr % 4) { // no need to check for stop, since we have large num
HEAP8[ptr++] = value;
}
if (value < 0) value += 256; // make it unsigned
var ptr4 = ptr >> 2, stop4 = stop >> 2, value4 = value | (value << 8) | (value << 16) | (value << 24);
while (ptr4 < stop4) {
HEAP32[ptr4++] = value4;
}
ptr = ptr4 << 2;
while (ptr < stop) {
HEAP8[ptr++] = value;
}
} else {
while (num--) {
HEAP8[ptr++] = value;
}
}
}
function _malloc(bytes) {
/* Over-allocate to make sure it is byte-aligned by 8.
* This will leak memory, but this is only the dummy
* implementation (replaced by dlmalloc normally) so
* not an issue.
*/
ptr = Runtime.staticAlloc(bytes + 8);
return (ptr+8) & 0xFFFFFFF8;
}
Module["_malloc"] = _malloc;
function _free(){}
Module["_free"] = _free;
__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });__ATEXIT__.push({ func: function() { FS.quit() } });
___setErrNo(0);
Module["requestFullScreen"] = function() { Browser.requestFullScreen() };
// === Auto-generated postamble setup entry stuff ===
Module.callMain = function callMain(args) {
var argc = args.length+1;
function pad() {
for (var i = 0; i < 4-1; i++) {
argv.push(0);
}
}
var argv = [allocate(intArrayFromString("/bin/this.program"), 'i8', ALLOC_STATIC) ];
pad();
for (var i = 0; i < argc-1; i = i + 1) {
argv.push(allocate(intArrayFromString(args[i]), 'i8', ALLOC_STATIC));
pad();
}
argv.push(0);
argv = allocate(argv, 'i32', ALLOC_STATIC);
return _main(argc, argv, 0);
}
FUNCTION_TABLE = [0,0]; Module["FUNCTION_TABLE"] = FUNCTION_TABLE;
function run(args) {
args = args || Module['arguments'];
if (Module['preRun']) {
Module['preRun']();
if (runDependencies > 0) {
// preRun added a dependency, run will be called later
Module['preRun'] = null;
return 0;
}
}
function doRun() {
var ret = 0;
if (Module['_main']) {
preMain();
ret = Module.callMain(args);
if (!Module['noExitRuntime']) {
exitRuntime();
}
}
if (Module['postRun']) {
Module['postRun']();
}
return ret;
}
if (Module['setStatus']) {
Module['setStatus']('Running...');
setTimeout(function() {
setTimeout(function() {
Module['setStatus']('');
}, 1);
doRun();
}, 1);
return 0;
} else {
return doRun();
}
}
Module['run'] = run;
// {{PRE_RUN_ADDITIONS}}
initRuntime();
if (Module['noInitialRun']) {
addRunDependency();
}
if (runDependencies == 0) {
var ret = run();
}
// {{POST_RUN_ADDITIONS}}
// {{MODULE_ADDITIONS}}
// EMSCRIPTEN_GENERATED_FUNCTIONS: ["_main"]
</script>
</body>
</html>
//"use strict";
// See browser tests for examples (tests/runner.py, search for sdl_). Run with
// python tests/runner.py browser
var LibrarySDL = {
$SDL__deps: ['$FS', '$Browser'],
$SDL: {
defaults: {
width: 320,
height: 200,
copyOnLock: true
},
version: null,
surfaces: {},
events: [],
audios: [null],
fonts: [null],
keyboardState: null,
shiftKey: false,
ctrlKey: false,
altKey: false,
startTime: null,
mouseX: 0,
mouseY: 0,
buttonState: 0,
DOMEventToSDLEvent: {},
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
38: 1106, // up arrow
40: 1105, // down arrow
37: 1104, // left arrow
39: 1103, // right arrow
33: 1099, // pagedup
34: 1102, // pagedown
17: 305, // control (right, or left)
18: 308, // alt
109: 45, // minus
16: 304, // shift
96: 88 | 1<<10, // keypad 0
97: 89 | 1<<10, // keypad 1
98: 90 | 1<<10, // keypad 2
99: 91 | 1<<10, // keypad 3
100: 92 | 1<<10, // keypad 4
101: 93 | 1<<10, // keypad 5
102: 94 | 1<<10, // keypad 6
103: 95 | 1<<10, // keypad 7
104: 96 | 1<<10, // keypad 8
105: 97 | 1<<10, // keypad 9
112: 58 | 1<<10, // F1
113: 59 | 1<<10, // F2
114: 60 | 1<<10, // F3
115: 61 | 1<<10, // F4
116: 62 | 1<<10, // F5
117: 63 | 1<<10, // F6
118: 64 | 1<<10, // F7
119: 65 | 1<<10, // F8
120: 66 | 1<<10, // F9
121: 67 | 1<<10, // F10
122: 68 | 1<<10, // F11
123: 69 | 1<<10, // F12
188: 44, // comma
190: 46, // period
191: 47, // slash (/)
192: 96, // backtick/backquote (`)
},
scanCodes: { // SDL keycode ==> SDL scancode. See SDL_scancode.h
97: 4, // A
98: 5,
99: 6,
100: 7,
101: 8,
102: 9,
103: 10,
104: 11,
105: 12,
106: 13,
107: 14,
108: 15,
109: 16,
110: 17,
111: 18,
112: 19,
113: 20,
114: 21,
115: 22,
116: 23,
117: 24,
118: 25,
119: 26,
120: 27,
121: 28,
122: 29, // Z
44: 54, // comma
46: 55, // period
47: 56, // slash
49: 30, // 1
50: 31,
51: 32,
52: 33,
53: 34,
54: 35,
55: 36,
56: 37,
57: 38, // 9
48: 39, // 0
13: 40, // return
9: 43, // tab
27: 41, // escape
32: 44, // space
92: 49, // backslash
305: 224, // ctrl
308: 226, // alt
},
structs: {
Rect: Runtime.generateStructInfo([
['i32', 'x'], ['i32', 'y'], ['i32', 'w'], ['i32', 'h'],
]),
PixelFormat: Runtime.generateStructInfo([
['i32', 'format'],
['void*', 'palette'], ['i8', 'BitsPerPixel'], ['i8', 'BytesPerPixel'],
['i8', 'padding1'], ['i8', 'padding2'],
['i32', 'Rmask'], ['i32', 'Gmask'], ['i32', 'Bmask'], ['i32', 'Amask'],
['i8', 'Rloss'], ['i8', 'Gloss'], ['i8', 'Bloss'], ['i8', 'Aloss'],
['i8', 'Rshift'], ['i8', 'Gshift'], ['i8', 'Bshift'], ['i8', 'Ashift']
]),
KeyboardEvent: Runtime.generateStructInfo([
['i32', 'type'],
['i32', 'windowID'],
['i8', 'state'],
['i8', 'repeat'],
['i8', 'padding2'],
['i8', 'padding3'],
['i32', 'keysym']
]),
keysym: Runtime.generateStructInfo([
['i32', 'scancode'],
['i32', 'sym'],
['i16', 'mod'],
['i32', 'unicode']
]),
MouseMotionEvent: Runtime.generateStructInfo([
['i32', 'type'],
['i32', 'windowID'],
['i8', 'state'],
['i8', 'padding1'],
['i8', 'padding2'],
['i8', 'padding3'],
['i32', 'x'],
['i32', 'y'],
['i32', 'xrel'],
['i32', 'yrel']
]),
MouseButtonEvent: Runtime.generateStructInfo([
['i32', 'type'],
['i32', 'windowID'],
['i8', 'button'],
['i8', 'state'],
['i8', 'padding1'],
['i8', 'padding2'],
['i32', 'x'],
['i32', 'y']
]),
AudioSpec: Runtime.generateStructInfo([
['i32', 'freq'],
['i16', 'format'],
['i8', 'channels'],
['i8', 'silence'],
['i16', 'samples'],
['i32', 'size'],
['void*', 'callback'],
['void*', 'userdata']
]),
version: Runtime.generateStructInfo([
['i8', 'major'],
['i8', 'minor'],
['i8', 'patch']
])
},
loadRect: function(rect) {
return {
x: {{{ makeGetValue('rect + SDL.structs.Rect.x', '0', 'i32') }}},
y: {{{ makeGetValue('rect + SDL.structs.Rect.y', '0', 'i32') }}},
w: {{{ makeGetValue('rect + SDL.structs.Rect.w', '0', 'i32') }}},
h: {{{ makeGetValue('rect + SDL.structs.Rect.h', '0', 'i32') }}}
};
},
// Load SDL color into a CSS-style color specification
loadColorToCSSRGB: function(color) {
var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
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) + ')';
},
translateColorToCSSRGBA: function(rgba) {
return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
},
translateRGBAToCSSRGBA: function(r, g, b, a) {
return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
},
makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) {
flags = flags || 0;
var surf = _malloc(14*Runtime.QUANTUM_SIZE); // SDL_Surface has 14 fields of quantum size
var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time
var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE);
flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} // SDL_Surface.flags
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*1', '0', 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*2', '0', 'width', 'i32') }}} // SDL_Surface.w
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*3', '0', 'height', 'i32') }}} // SDL_Surface.h
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*4', '0', 'width*4', 'i32') }}} // SDL_Surface.pitch, assuming RGBA for now,
// since that is what ImageData gives us in browsers
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*5', '0', 'buffer', 'void*') }}} // SDL_Surface.pixels
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*6', '0', '0', 'i32*') }}} // SDL_Surface.offset
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.format', '0', '-2042224636', 'i32') }}} // SDL_PIXELFORMAT_RGBA8888
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.palette', '0', '0', 'i32') }}} // TODO
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', '32', 'i8') }}} // TODO
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BytesPerPixel', '0', '4', 'i8') }}} // TODO
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Rmask', '0', 'rmask || 0x000000ff', 'i32') }}}
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Gmask', '0', 'gmask || 0x0000ff00', 'i32') }}}
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Bmask', '0', 'bmask || 0x00ff0000', 'i32') }}}
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Amask', '0', 'amask || 0xff000000', 'i32') }}}
// Decide if we want to use WebGL or not
var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL
var canvas;
if (!usePageCanvas) {
canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
} else {
canvas = Module['canvas'];
}
var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas);
SDL.surfaces[surf] = {
width: width,
height: height,
canvas: canvas,
ctx: ctx,
surf: surf,
buffer: buffer,
pixelFormat: pixelFormat,
alpha: 255,
flags: flags,
locked: 0,
usePageCanvas: usePageCanvas,
source: source
};
return surf;
},
freeSurface: function(surf) {
_free(SDL.surfaces[surf].buffer);
_free(SDL.surfaces[surf].pixelFormat);
_free(surf);
SDL.surfaces[surf] = null;
},
receiveEvent: function(event) {
switch(event.type) {
case 'mousemove':
// workaround for firefox bug 750111
event['movementX'] = event['mozMovementX'];
event['movementY'] = event['mozMovementY'];
// fall through
case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'DOMMouseScroll':
if (event.type == 'DOMMouseScroll') {
event = {
type: 'mousedown',
button: event.detail > 0 ? 4 : 3,
pageX: event.pageX,
pageY: event.pageY
};
}
SDL.events.push(event);
if (SDL.events.length >= 10000) {
Module.printErr('SDL event queue full, dropping earliest event');
SDL.events.shift();
}
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;
}
return false;
},
makeCEvent: function(event, ptr) {
if (typeof event === 'number') {
// This is a pointer to a native C event that was SDL_PushEvent'ed
_memcpy(ptr, event, SDL.structs.KeyboardEvent.__size__); // XXX
return;
}
switch(event.type) {
case 'keydown': case 'keyup': {
var down = event.type === 'keydown';
//Module.print('Received key event: ' + event.keyCode);
var key = event.keyCode;
if (key >= 65 && key <= 90) {
key += 32; // make lowercase for SDL
} else {
key = SDL.keyCodes[event.keyCode] || event.keyCode;
}
var scan;
if (key >= 1024) {
scan = key - 1024;
} else {
scan = SDL.scanCodes[key] || key;
}
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}
//{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.which', '1', 'i32') }}}
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}}
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}}
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}}
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', '0', 'i32') }}}
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'key', 'i32') }}}
{{{ makeSetValue('SDL.keyboardState', 'SDL.keyCodes[event.keyCode] || event.keyCode', 'event.type == "keydown"', 'i8') }}};
SDL.shiftKey = event.shiftKey;
SDL.ctrlKey = event.ctrlKey;
SDL.altKey = event.altKey;
break;
}
case 'mousedown': case 'mouseup':
if (event.type == 'mousedown') {
// SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3,
// and DOM buttons are 0-2, so this means that the below formula is
// correct.
SDL.buttonState |= 1 << event.button;
} else if (event.type == 'mouseup') {
SDL.buttonState = 0;
}
// fall through
case 'mousemove': {
var x = event.pageX - Module['canvas'].offsetLeft;
var y = event.pageY - Module['canvas'].offsetTop;
if (event.type != 'mousemove') {
var down = event.type === 'mousedown';
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.button', 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.state', 'down ? 1 : 0', 'i8') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.x', 'x', 'i32') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.y', 'y', 'i32') }}};
} else {
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.state', 'SDL.buttonState', 'i8') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.x', 'x', 'i32') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.y', 'y', 'i32') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.xrel', 'Browser.getMovementX(x - SDL.mouseX, event)', 'i32') }}};
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'Browser.getMovementY(y - SDL.mouseY, event)', 'i32') }}};
}
SDL.mouseX = x;
SDL.mouseY = y;
break;
}
default:
throw 'Unhandled SDL event: ' + event.type;
}
},
estimateTextWidth: function(fontData, text) {
var h = fontData.size;
var fontString = h + 'px sans-serif';
// TODO: use temp context, not screen's, to avoid affecting its performance?
var tempCtx = SDL.surfaces[SDL.screen].ctx;
tempCtx.save();
tempCtx.font = fontString;
var ret = tempCtx.measureText(text).width | 0;
tempCtx.restore();
return ret;
},
// Sound
allocateChannels: function(num) { // called from Mix_AllocateChannels and init
if (SDL.numChannels && SDL.numChannels >= num) return;
SDL.numChannels = num;
SDL.channels = [];
for (var i = 0; i < num; i++) {
SDL.channels[i] = {
audio: null,
volume: 1.0
};
}
},
// Debugging
debugSurface: function(surfData) {
console.log('dumping surface ' + [surfData.surf, surfData.source, surfData.width, surfData.height]);
var image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
var data = image.data;
var num = Math.min(surfData.width, surfData.height);
for (var i = 0; i < num; i++) {
console.log(' diagonal ' + i + ':' + [data[i*surfData.width*4 + i*4 + 0], data[i*surfData.width*4 + i*4 + 1], data[i*surfData.width*4 + i*4 + 2], data[i*surfData.width*4 + i*4 + 3]]);
}
}
},
SDL_Linked_Version: function() {
if (SDL.version === null) {
SDL.version = _malloc(SDL.structs.version.__size__);
{{{ makeSetValue('SDL.version + SDL.structs.version.major', '0', '1', 'i8') }}}
{{{ makeSetValue('SDL.version + SDL.structs.version.minor', '0', '3', 'i8') }}}
{{{ makeSetValue('SDL.version + SDL.structs.version.patch', '0', '0', 'i8') }}}
}
return SDL.version;
},
SDL_Init: function(what) {
SDL.startTime = Date.now();
['keydown', 'keyup'].forEach(function(event) {
addEventListener(event, SDL.receiveEvent, true);
});
SDL.keyboardState = _malloc(0x10000);
_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['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
return 0; // success
},
SDL_WasInit__deps: ['SDL_Init'],
SDL_WasInit: function() {
if (SDL.startTime === null) {
_SDL_Init();
}
return 1;
},
SDL_GetVideoInfo: function() {
// %struct.SDL_VideoInfo = type { i32, i32, %struct.SDL_PixelFormat*, i32, i32 } - 5 fields of quantum size
var ret = _malloc(5*Runtime.QUANTUM_SIZE);
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}}
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'SDL.defaults.width', 'i32') }}}
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'SDL.defaults.height', 'i32') }}}
return ret;
},
SDL_ListModes: function(format, flags) {
return -1; // -1 == all modes are ok. TODO
},
SDL_SetVideoMode: function(width, height, depth, flags) {
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
Module['canvas'].width = width;
Module['canvas'].height = height;
return SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
},
SDL_Quit: function() {
for (var i = 0; i < SDL.audios; i++) {
SDL.audios[i].pause();
}
Module.print('SDL_Quit called (and ignored)');
},
// Copy data from the canvas backing to a C++-accessible storage
SDL_LockSurface: function(surf) {
var surfData = SDL.surfaces[surf];
surfData.locked++;
if (surfData.locked > 1) return 0;
if (!surfData.image) {
surfData.image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
if (surf == SDL.screen) {
var data = surfData.image.data;
var num = data.length;
for (var i = 0; i < num/4; i++) {
data[i*4+3] = 255; // opacity, as canvases blend alpha
}
}
}
if (SDL.defaults.copyOnLock) {
// Copy pixel data to somewhere accessible to 'C/C++'
#if USE_TYPED_ARRAYS == 2
HEAPU8.set(surfData.image.data, surfData.buffer);
#else
var num2 = surfData.image.data.length;
for (var i = 0; i < num2; i++) {
{{{ makeSetValue('surfData.buffer', 'i', 'surfData.image.data[i]', 'i8') }}};
}
#endif
}
// Mark in C/C++-accessible SDL structure
// SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ...
// So we have fields all of the same size, and 5 of them before us.
// TODO: Use macros like in library.js
{{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}};
return 0;
},
// Copy data from the C++-accessible storage to the canvas backing
SDL_UnlockSurface: function(surf) {
var surfData = SDL.surfaces[surf];
surfData.locked--;
if (surfData.locked > 0) return;
// Copy pixel data to image
var num = surfData.image.data.length;
if (!surfData.colors) {
var data = surfData.image.data;
var buffer = surfData.buffer;
#if USE_TYPED_ARRAYS == 2
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+1] = (val >> 8) & 0xff;
data[dst+2] = (val >> 16) & 0xff;
data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
src++;
dst += 4;
}
#else
for (var i = 0; i < num; i++) {
// We may need to correct signs here. Potentially you can hardcode a write of 255 to alpha, say, and
// the compiler may decide to write -1 in the llvm bitcode...
data[i] = {{{ makeGetValue('buffer', 'i', 'i8') + (CORRECT_SIGNS ? '&0xff' : '') }}};
if (i % 4 == 3) data[i] = 0xff;
}
#endif
} else {
var width = Module['canvas'].width;
var height = Module['canvas'].height;
var s = surfData.buffer;
var data = surfData.image.data;
var colors = surfData.colors;
for (var y = 0; y < height; y++) {
var base = y*width*4;
for (var x = 0; x < width; x++) {
// See comment above about signs
var val = {{{ makeGetValue('s++', '0', 'i8') + (CORRECT_SIGNS ? '&0xff' : '') }}};
var color = colors[val] || [Math.floor(Math.random()*255),Math.floor(Math.random()*255),Math.floor(Math.random()*255)]; // XXX
var start = base + x*4;
data[start] = color[0];
data[start+1] = color[1];
data[start+2] = color[2];
}
s += width*3;
}
}
// Copy to canvas
surfData.ctx.putImageData(surfData.image, 0, 0);
// Note that we save the image, so future writes are fast. But, memory is not yet released
},
SDL_Flip: function(surf) {
// We actually do this in Unlock, since the screen surface has as its canvas
// backing the page canvas element
},
SDL_UpdateRect: function(surf, x, y, w, h) {
// We actually do the whole screen in Unlock...
},
SDL_Delay: function(delay) {
throw 'SDL_Delay called! Potential infinite loop, quitting. ' + new Error().stack;
},
SDL_WM_SetCaption: function(title, icon) {
title = title && Pointer_stringify(title);
icon = icon && Pointer_stringify(icon);
},
SDL_EnableKeyRepeat: function(delay, interval) {
// TODO
},
SDL_GetKeyboardState: function() {
return SDL.keyboardState;
},
SDL_GetModState: function() {
// TODO: numlock, capslock, etc.
return (SDL.shiftKey ? 0x0001 & 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT
(SDL.ctrlKey ? 0x0040 & 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL
(SDL.altKey ? 0x0100 & 0x0200 : 0); // KMOD_LALT & KMOD_RALT
},
SDL_GetMouseState: function(x, y) {
if (x) {{{ makeSetValue('x', '0', 'SDL.mouseX', 'i32') }}};
if (y) {{{ makeSetValue('y', '0', 'SDL.mouseY', 'i32') }}};
return 0;
},
SDL_WarpMouse: function(x, y) {
return; // TODO: implement this in a non-buggy way. Need to keep relative mouse movements correct after calling this
SDL.events.push({
type: 'mousemove',
pageX: x + Module['canvas'].offsetLeft,
pageY: y + Module['canvas'].offsetTop
});
},
SDL_ShowCursor: function(toggle) {
// TODO
},
SDL_GetError: function() {
return allocate(intArrayFromString("SDL is cool"), 'i8');
},
SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) {
return SDL.makeSurface(width, height, flags, false, 'CreateRGBSurface', rmask, gmask, bmask, amask);
},
SDL_DisplayFormatAlpha: function(surf) {
var oldData = SDL.surfaces[surf];
var ret = SDL.makeSurface(oldData.width, oldData.height, oldData.flags, false, 'copy:' + oldData.source);
var newData = SDL.surfaces[ret];
//newData.ctx.putImageData(oldData.ctx.getImageData(0, 0, oldData.width, oldData.height), 0, 0);
newData.ctx.drawImage(oldData.canvas, 0, 0);
return ret;
},
SDL_FreeSurface: function(surf) {
if (surf) SDL.freeSurface(surf);
},
SDL_UpperBlit: function(src, srcrect, dst, dstrect) {
var srcData = SDL.surfaces[src];
var dstData = SDL.surfaces[dst];
var sr, dr;
if (srcrect) {
sr = SDL.loadRect(srcrect);
} else {
sr = { x: 0, y: 0, w: srcData.width, h: srcData.height };
}
if (dstrect) {
dr = SDL.loadRect(dstrect);
} else {
dr = { x: 0, y: 0, w: -1, h: -1 };
}
dstData.ctx.drawImage(srcData.canvas, sr.x, sr.y, sr.w, sr.h, dr.x, dr.y, sr.w, sr.h);
if (dst != SDL.screen) {
// XXX As in IMG_Load, for compatibility we write out |pixels|
console.log('WARNING: copying canvas data to memory for compatibility');
_SDL_LockSurface(dst);
dstData.locked--; // The surface is not actually locked in this hack
}
return 0;
},
SDL_FillRect: function(surf, rect, color) {
var surfData = SDL.surfaces[surf];
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.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_UpperBlit(src, srcrect, dst, dstrect);
},
SDL_SetAlpha: function(surf, flag, alpha) {
SDL.surfaces[surf].alpha = alpha;
},
SDL_GetTicks: function() {
return Math.floor(Date.now() - SDL.startTime);
},
SDL_PollEvent: function(ptr) {
if (SDL.events.length === 0) return 0;
if (ptr) {
SDL.makeCEvent(SDL.events.shift(), ptr);
}
return 1;
},
SDL_PushEvent: function(ptr) {
SDL.events.push(ptr); // XXX Should we copy it? Not clear from API
return 0;
},
SDL_PeepEvents: function(events, numEvents, 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(..)
}
return got;
}
default: throw 'SDL_PeepEvents does not yet support that action: ' + action;
}
},
SDL_PumpEvents: function(){},
SDL_SetColors: function(surf, colors, firstColor, nColors) {
var surfData = SDL.surfaces[surf];
surfData.colors = [];
for (var i = firstColor; i < nColors; i++) {
surfData.colors[i] = Array_copy(colors + i*4, colors + i*4 + 4);
}
return 1;
},
SDL_MapRGB: function(fmt, r, g, b) {
// Canvas screens are always RGBA
return r + (g << 8) + (b << 16);
},
SDL_WM_GrabInput: function() {},
// SDL_Image
IMG_Init: function(flags) {
return flags; // We support JPG, PNG, TIF because browsers do
},
IMG_Load__deps: ['SDL_LockSurface'],
IMG_Load: function(filename) {
filename = FS.standardizePath(Pointer_stringify(filename));
if (filename[0] == '/') {
// Convert the path to relative
filename = filename.substr(1);
}
var raw = preloadedImages[filename];
if (!raw) {
Runtime.warnOnce('Cannot find preloaded image ' + filename);
return 0;
}
var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
var surfData = SDL.surfaces[surf];
surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height);
// XXX SDL does not specify that loaded images must have available pixel data, in fact
// there are cases where you just want to blit them, so you just need the hardware
// accelerated version. However, code everywhere seems to assume that the pixels
// are in fact available, so we retrieve it here. This does add overhead though.
_SDL_LockSurface(surf);
surfData.locked--; // The surface is not actually locked in this hack
return surf;
},
SDL_LoadBMP: 'IMG_Load',
SDL_LoadBMP_RW: 'IMG_Load',
// SDL_Audio
SDL_OpenAudio: function(desired, obtained) {
SDL.allocateChannels(32);
// FIXME: Assumes 16-bit audio
assert(obtained === 0, 'Cannot return obtained SDL audio params');
SDL.audio = {
freq: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.freq', 'i32', 0, 1) }}},
format: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.format', 'i16', 0, 1) }}},
channels: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.channels', 'i8', 0, 1) }}},
samples: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.samples', 'i16', 0, 1) }}},
callback: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.callback', 'void*', 0, 1) }}},
userdata: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.userdata', 'void*', 0, 1) }}},
paused: true,
timer: null
};
var totalSamples = SDL.audio.samples*SDL.audio.channels;
SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio
SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
SDL.audio.caller = function() {
FUNCTION_TABLE[SDL.audio.callback](SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize);
SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
};
// Mozilla Audio API. TODO: Other audio APIs
try {
SDL.audio.mozOutput = new Audio();
SDL.audio.mozOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
SDL.audio.mozBuffer = new Float32Array(totalSamples);
SDL.audio.pushAudio = function(ptr, size) {
var mozBuffer = SDL.audio.mozBuffer;
for (var i = 0; i < totalSamples; i++) {
mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
}
SDL.audio.mozOutput['mozWriteAudio'](mozBuffer);
}
} catch(e) {
SDL.audio = null;
}
if (!SDL.audio) return -1;
return 0;
},
SDL_PauseAudio: function(pauseOn) {
if (SDL.audio.paused !== pauseOn) {
SDL.audio.timer = pauseOn ? SDL.audio.timer && clearInterval(SDL.audio.timer) : setInterval(SDL.audio.caller, 1/35);
}
SDL.audio.paused = pauseOn;
},
SDL_CloseAudio: function() {
if (SDL.audio) {
_SDL_PauseAudio(1);
_free(SDL.audio.buffer);
SDL.audio = null;
}
},
SDL_LockAudio: function() {},
SDL_UnlockAudio: function() {},
SDL_CreateMutex: function() { return 0 },
SDL_LockMutex: function() {},
SDL_UnlockMutex: function() {},
SDL_DestroyMutex: function() {},
SDL_CreateCond: function() { return 0 },
SDL_CondSignal: function() {},
SDL_CondWait: function() {},
SDL_DestroyCond: function() {},
// SDL Mixer
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
SDL.allocateChannels(32);
return 0;
},
Mix_CloseAudio: 'SDL_CloseAudio',
Mix_AllocateChannels: function(num) {
SDL.allocateChannels(num);
return num;
},
Mix_ChannelFinished: function(func) {
SDL.channelFinished = func;
},
Mix_Volume: function(channel, volume) {
var info = SDL.channels[channel];
var ret = info.volume * 128;
info.volume = volume / 128;
if (info.audio) info.audio.volume = info.volume;
return ret;
},
Mix_SetPanning: function() {
return 0; // error
},
Mix_LoadWAV_RW: function(filename, freesrc) {
filename = FS.standardizePath(Pointer_stringify(filename));
var raw = preloadedAudios[filename];
if (!raw) {
Runtime.warnOnce('Cannot find preloaded audio ' + filename);
return 0;
}
var id = SDL.audios.length;
SDL.audios.push({
source: filename,
audio: raw
});
return id;
},
Mix_FreeChunk: function(id) {
SDL.audios[id] = null;
},
Mix_PlayChannel: function(channel, id, loops) {
// TODO: handle loops
var audio = SDL.audios[id].audio;
if (!audio) return 0;
if (channel == -1) {
channel = 0;
for (var i = 0; i < SDL.numChannels; i++) {
if (!SDL.channels[i].audio) {
channel = i;
break;
}
}
}
var info = SDL.channels[channel];
info.audio = audio.cloneNode(true);
if (SDL.channelFinished) {
info.audio['onended'] = function() { // TODO: cache these
Runtime.getFuncWrapper(SDL.channelFinished)(channel);
}
}
info.audio.play();
info.audio.volume = info.volume;
return channel;
},
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
Mix_FadingChannel: function(channel) {
return 0; // MIX_NO_FADING, TODO
},
Mix_HaltChannel: function(channel) {
var info = SDL.channels[channel];
if (info.audio) {
info.audio.pause();
info.audio = null;
}
if (SDL.channelFinished) {
Runtime.getFuncWrapper(SDL.channelFinished)(channel);
}
return 0;
},
Mix_HookMusicFinished: function(func) {
SDL.hookMusicFinished = func;
},
Mix_VolumeMusic: function(func) {
return 0; // TODO
},
Mix_LoadMUS: 'Mix_LoadWAV_RW',
Mix_FreeMusic: 'Mix_FreeChunk',
Mix_PlayMusic: function(id, loops) {
loops = Math.max(loops, 1);
var audio = SDL.audios[id].audio;
if (!audio) return 0;
audio.loop = loops != 1; // TODO: handle N loops for finite N
audio.play();
SDL.music = audio;
return 0;
},
Mix_PauseMusic: function(id) {
var audio = SDL.audios[id];
if (!audio) return 0;
audio.audio.pause();
return 0;
},
Mix_ResumeMusic: function(id) {
var audio = SDL.audios[id];
if (!audio) return 0;
audio.audio.play();
return 0;
},
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: 'Mix_HaltMusic', // XXX ignore fading out effect
// SDL TTF
TTF_Init: function() { return 0 },
TTF_OpenFont: function(filename, size) {
filename = FS.standardizePath(Pointer_stringify(filename));
var id = SDL.fonts.length;
SDL.fonts.push({
name: filename, // but we don't actually do anything with it..
size: size
});
return id;
},
TTF_RenderText_Solid: function(font, text, color) {
// XXX the font and color are ignored
text = Pointer_stringify(text) || ' '; // if given an empty string, still return a valid surface
var fontData = SDL.fonts[font];
var w = SDL.estimateTextWidth(fontData, text);
var h = fontData.size;
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];
surfData.ctx.save();
surfData.ctx.fillStyle = color;
surfData.ctx.font = fontString;
surfData.ctx.textBaseline = 'top';
surfData.ctx.fillText(text, 0, 0);
surfData.ctx.restore();
return surf;
},
TTF_RenderText_Blended: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
TTF_RenderText_Shaded: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
TTF_SizeText: function(font, text, w, h) {
var fontData = SDL.fonts[font];
if (w) {
{{{ makeSetValue('w', '0', 'SDL.estimateTextWidth(fontData, Pointer_stringify(text))', 'i32') }}};
}
if (h) {
{{{ makeSetValue('h', '0', 'fontData.size', 'i32') }}};
}
return 0;
},
TTF_FontAscent: function(font) {
var fontData = SDL.fonts[font];
return Math.floor(fontData.size*0.98); // XXX
},
TTF_FontDescent: function(font) {
var fontData = SDL.fonts[font];
return Math.floor(fontData.size*0.02); // XXX
},
// SDL gfx
boxRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
var surfData = SDL.surfaces[surf];
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.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
surfData.ctx.restore();
},
rectangleRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
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.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
surfData.ctx.restore();
},
lineRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
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.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.beginPath();
surfData.ctx.moveTo(x1, y1);
surfData.ctx.lineTo(x2, y2);
surfData.ctx.stroke();
surfData.ctx.restore();
},
circleRGBA: function(surf, x, y, rad, r, g, b, a) {
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.translateRGBAToCSSRGBA(r, g, b, a);
surfData.ctx.arc(x, y, rad, 0, 2 * Math.PI, false);
surfData.ctx.stroke();
surfData.ctx.restore();
},
pixelRGBA__deps: ['boxRGBA'],
pixelRGBA: function(surf, x1, y1, r, g, b, a) {
// This cannot be fast, to render many pixels this way!
_boxRGBA(surf, x1, y1, x1, y1, r, g, b, a);
},
// GL
SDL_GL_SetAttribute: function(attr, value) {
console.log('TODO: SDL_GL_SetAttribute');
},
SDL_GL_GetProcAddress__deps: ['$GLEmulation'],
SDL_GL_GetProcAddress: function(name_) {
return GLEmulation.getProcAddress(Pointer_stringify(name_));
},
SDL_GL_SwapBuffers: function() {},
// Misc
SDL_InitSubSystem: function(flags) { return 0 },
SDL_NumJoysticks: function() { return 0 },
SDL_RWFromFile: function(filename, mode) {
return filename; // XXX We just forward the filename
},
SDL_EnableUNICODE: function(on) {
var ret = SDL.unicode || 0;
SDL.unicode = on;
return ret;
},
SDL_AddTimer: function(interval, callback, param) {
return window.setTimeout(function() {
FUNCTION_TABLE[callback](interval, param);
}, interval);
},
SDL_RemoveTimer: function(id) {
window.clearTimeout(id);
return true;
}
};
autoAddDeps(LibrarySDL, '$SDL');
mergeInto(LibraryManager.library, LibrarySDL);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment