aboutsummaryrefslogtreecommitdiff
path: root/src/library_sdl.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library_sdl.js')
-rw-r--r--src/library_sdl.js716
1 files changed, 598 insertions, 118 deletions
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 7b413c13..f71527af 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -75,50 +75,146 @@
//
// * SDL_Quit does nothing.
-mergeInto(LibraryManager.library, {
- $SDL__deps: ['$Browser'],
+var LibrarySDL = {
+ $SDL__deps: ['$FS', '$Browser'],
$SDL: {
defaults: {
width: 320,
height: 200,
- copyScreenOnLock: false
+ copyOnLock: true
},
+ version: null,
+
surfaces: {},
events: [],
+ audios: [null],
+ fonts: [null],
+
+ keyboardState: null,
+ startTime: null,
+ mouseX: 0,
+ mouseY: 0,
+
+ DOMEventToSDLEvent: {
+ 'keydown': 0x300,
+ 'keyup': 0x301,
+ 'mousedown': 0x401,
+ 'mouseup': 0x402,
+ 'mousemove': 0x400
+ },
+
+ keyCodes: { // DOM code ==> SDL code
+ 38: 1106, // up arrow
+ 40: 1105, // down arrow
+ 37: 1104, // left arrow
+ 39: 1103, // right arrow
+
+ 33: 1099, // pagedup
+ 34: 1102, // pagedown
- keyCodes: {
- 38: 273, // up arrow
- 40: 274, // down arrow
- 37: 276, // left arrow
- 39: 275, // right arrow
17: 305, // control (right, or left)
18: 308, // alt
109: 45, // minus
16: 304 // shift
},
+ scanCodes: { // SDL keycode ==> SDL scancode
+ 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
+ 48: 30, // 0
+ 49: 31,
+ 50: 32,
+ 51: 33,
+ 52: 34,
+ 53: 35,
+ 54: 36,
+ 55: 37,
+ 56: 38,
+ 57: 39, // 9
+ 13: 40, // return
+ 9: 43, // tab
+ 32: 44, // space
+ 92: 49, // backslash
+ 47: 56, // slash
+ 1106: 82, // up arrow
+ 1105: 81, // down arrow
+ 1104: 80, // left arrow
+ 1103: 79 // right arrow
+ },
+
structs: {
Rect: Runtime.generateStructInfo([
- ['i16', 'x'], ['i16', 'y'], ['i16', 'w'], ['i16', 'h'],
+ ['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'],
- ['i32', 'Rmask'], ['i32', 'Gmask'], ['i32', 'Bmask'], ['i32', 'Amask'] // Docs say i8, ./include/SDL_video.h says i32...
+ ['i8', 'Rshift'], ['i8', 'Gshift'], ['i8', 'Bshift'], ['i8', 'Ashift']
]),
KeyboardEvent: Runtime.generateStructInfo([
- ['i8', 'type'],
- ['i8', 'which'],
+ ['i32', 'type'],
+ ['i32', 'windowID'],
['i8', 'state'],
+ ['i8', 'repeat'],
+ ['i8', 'padding2'],
+ ['i8', 'padding3'],
['i32', 'keysym']
]),
keysym: Runtime.generateStructInfo([
- ['i8', 'scancode'],
+ ['i32', 'scancode'],
['i32', 'sym'],
- ['i32', 'mod'],
- ['i16', 'unicode']
+ ['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'],
@@ -129,12 +225,45 @@ mergeInto(LibraryManager.library, {
['i32', 'size'],
['void*', 'callback'],
['void*', 'userdata']
+ ]),
+ version: Runtime.generateStructInfo([
+ ['i8', 'major'],
+ ['i8', 'minor'],
+ ['i8', 'patch']
])
},
- makeSurface: function(width, height, flags) {
+ 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);
+ 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
@@ -142,70 +271,69 @@ mergeInto(LibraryManager.library, {
{{{ 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', 'i16') }}} // SDL_Surface.pitch, assuming RGBA for now,
+ {{{ 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', '0xff', 'i32') }}}
- {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Gmask', '0', '0xff', 'i32') }}}
- {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Bmask', '0', '0xff', 'i32') }}}
- {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Amask', '0', '0xff', 'i32') }}}
+ {{{ 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);
+ if (usePageCanvas) {
+ Module.ctx = ctx;
+ }
SDL.surfaces[surf] = {
width: width,
height: height,
- canvas: Module['canvas'],
- ctx: SDL.createContext(useWebGL),
+ canvas: canvas,
+ ctx: ctx,
surf: surf,
buffer: buffer,
pixelFormat: pixelFormat,
- alpha: 255
+ alpha: 255,
+ flags: flags,
+ locked: 0,
+ usePageCanvas: usePageCanvas,
+ source: source
};
return surf;
},
- createContext: function(useWebGL) {
-#if !USE_TYPED_ARRAYS
- if (useWebGL) {
- Module.print('(USE_TYPED_ARRAYS needs to be enabled for WebGL)');
- return null;
- }
-#endif
- try {
- var ctx = Module.canvas.getContext(useWebGL ? 'experimental-webgl' : '2d');
- if (!ctx) throw 'Could not create canvas :(';
- if (useWebGL) {
- // Set the background of the WebGL canvas to black, because SDL gives us a
- // window which has a black background by default.
- Module.canvas.style.backgroundColor = "black";
- }
- return Module.ctx = ctx;
- } catch (e) {
- Module.print('(canvas not available)');
- return null;
- }
- },
-
freeSurface: function(surf) {
_free(SDL.surfaces[surf].buffer);
_free(SDL.surfaces[surf].pixelFormat);
_free(surf);
- delete SDL.surfaces[surf];
+ SDL.surfaces[surf] = null;
},
receiveEvent: function(event) {
switch(event.type) {
- case 'keydown': case 'keyup':
- //print('zz receive Event: ' + event.keyCode);
+ case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'mousemove':
SDL.events.push(event);
+ if ((event.keyCode >= 37 && event.keyCode <= 40) || // arrow keys
+ event.keyCode == 32 || // space
+ event.keyCode == 33 || event.keyCode == 34) { // page up/down
+ event.preventDefault();
+ }
+ break;
}
//event.preventDefault();
return false;
@@ -214,45 +342,113 @@ mergeInto(LibraryManager.library, {
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__);
+ _memcpy(ptr, event, SDL.structs.KeyboardEvent.__size__); // XXX
return;
}
switch(event.type) {
- case 'keydown': case 'keyup':
+ case 'keydown': case 'keyup': {
var down = event.type === 'keydown';
+ //Module.print('Received key event: ' + event.keyCode);
var key = SDL.keyCodes[event.keyCode] || event.keyCode;
if (key >= 65 && key <= 90) {
key = String.fromCharCode(key).toLowerCase().charCodeAt(0);
}
- //print('zz passing over Event: ' + key);
- {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'down ? 2 : 3', 'i8') }}}
- {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.which', '1', 'i8') }}}
+ var 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', 'key', 'i8') }}}
+ {{{ 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') }}};
+
break;
- case 'keypress': break // TODO
+ }
+ case 'mousedown': case 'mouseup': 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.button', 'event.button', 'i8') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.state', 'down ? 1 : 0', 'i8') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.x', 'x', 'i32') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.y', 'y', 'i32') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.xrel', 'x - SDL.mouseX', 'i32') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'y - SDL.mouseY', '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;
+ },
+
+ // 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__deps: ['$SDL'],
SDL_Init: function(what) {
SDL.startTime = Date.now();
- ['keydown', 'keyup', 'keypress'].forEach(function(event) {
+ ['keydown', 'keyup'].forEach(function(event) {
addEventListener(event, SDL.receiveEvent, true);
});
+ SDL.keyboardState = _malloc(0x10000);
+ _memset(SDL.keyboardState, 0, 0x10000);
return 0; // success
},
- SDL_WasInit: function() { return 0 }, // TODO
+ 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
@@ -269,46 +465,66 @@ mergeInto(LibraryManager.library, {
return -1; // -1 == all modes are ok. TODO
},
- SDL_GL_SetAttribute: function(attr, value) {
- // TODO
- },
-
SDL_SetVideoMode: function(width, height, depth, flags) {
+ ['mousedown', 'mouseup', 'mousemove'].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);
+ return SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
},
SDL_Quit: function() {
- print('SDL_Quit called (and ignored)');
+ 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);
- 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 (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.copyScreenOnLock) {
+ 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) {
@@ -318,12 +534,14 @@ mergeInto(LibraryManager.library, {
assert(buffer % 4 == 0, 'Invalid buffer offset: ' + buffer);
var src = buffer >> 2;
var dst = 0;
+ var isScreen = surf == SDL.screen;
while (dst < num) {
+ // TODO: access underlying data buffer and write in 32-bit chunks or more
var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
- data[dst] = val & 0xff;
+ data[dst ] = val & 0xff;
data[dst+1] = (val >> 8) & 0xff;
data[dst+2] = (val >> 16) & 0xff;
- data[dst+3] = 0xff;
+ data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
src++;
dst += 4;
}
@@ -361,7 +579,8 @@ mergeInto(LibraryManager.library, {
},
SDL_Flip: function(surf) {
- // We actually do this in Unlock...
+ // 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) {
@@ -369,7 +588,7 @@ mergeInto(LibraryManager.library, {
},
SDL_Delay: function(delay) {
- print('SDL_Delay called! - potential infinite loop');
+ throw 'SDL_Delay called! Potential infinite loop, quitting. ' + new Error().stack;
},
SDL_WM_SetCaption: function(title, icon) {
@@ -381,6 +600,16 @@ mergeInto(LibraryManager.library, {
// TODO
},
+ SDL_GetKeyboardState: function() {
+ return SDL.keyboardState;
+ },
+
+ SDL_GetMouseState: function(x, y) {
+ if (x) {{{ makeSetValue('x', '0', 'SDL.mouseX', 'i32') }}};
+ if (y) {{{ makeSetValue('y', '0', 'SDL.mouseY', 'i32') }}};
+ return 0;
+ },
+
SDL_ShowCursor: function(toggle) {
// TODO
},
@@ -390,55 +619,65 @@ mergeInto(LibraryManager.library, {
},
SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) {
- return SDL.makeSurface(width, height, flags);
+ 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) {
- SDL.freeSurface(surf);
+ if (surf) SDL.freeSurface(surf);
},
SDL_UpperBlit: function(src, srcrect, dst, dstrect) {
- assert(!srcrect && !dstrect); // TODO
var srcData = SDL.surfaces[src];
var dstData = SDL.surfaces[dst];
- assert(srcData.width === dstData.width && srcData.height === dstData.height);
- {{{ makeCopyValues('dstData.buffer', 'srcData.buffer', 'srcData.width*srcData.height*4', 'i8', null, 1) }}}
+ 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];
- var c1 = color & 0xff;
- var c2 = (color >> 8) & 0xff;
- var c3 = (color >> 16) & 0xff;
- var rx = {{{ makeGetValue('rect + SDL.structs.Rect.x', '0', 'i16') }}};
- var ry = {{{ makeGetValue('rect + SDL.structs.Rect.y', '0', 'i16') }}};
- var rw = {{{ makeGetValue('rect + SDL.structs.Rect.w', '0', 'i16') }}};
- var rh = {{{ makeGetValue('rect + SDL.structs.Rect.h', '0', 'i16') }}};
- var data = surfData.image.data;
- var width = surfData.width;
- for (var y = ry; y < ry + rh; y++) {
- var base = y*width*4;
- for (var x = rx; x < rx + rw; x++) {
- var start = x*4 + base;
- data[start] = c1;
- data[start+1] = c2;
- data[start+2] = c3;
- }
- }
+ 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_Blit(src, srcrect, dst, dstrect);
+ return _SDL_UpperBlit(src, srcrect, dst, dstrect);
},
SDL_SetAlpha: function(surf, flag, alpha) {
SDL.surfaces[surf].alpha = alpha;
},
- SDL_GL_SwapBuffers: function() {},
-
SDL_GetTicks: function() {
return Math.floor(Date.now() - SDL.startTime);
},
@@ -453,10 +692,30 @@ mergeInto(LibraryManager.library, {
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 = [];
@@ -476,18 +735,26 @@ mergeInto(LibraryManager.library, {
// SDL_Image
+ IMG_Load__deps: ['SDL_LockSurface'],
IMG_Load: function(filename) {
- filename = Pointer_stringify(filename);
- var format = filename.split('.').slice(-1)[0];
- var data = readBinary(filename);
- var raw = Browser.decodeImage(data, format);
- var surf = SDL.makeSurface(raw.width, raw.height, 0);
- // XXX Extremely inefficient!
- for (var i = 0; i < raw.width*raw.height*4; i++) {
- {{{ makeSetValue('SDL.surfaces[surf].buffer', 'i', 'raw.data[i]', 'i8') }}}
+ filename = FS.standardizePath(Pointer_stringify(filename));
+ var raw = preloadedImages[filename];
+ if (!raw) {
+ Module.printErr('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_Audio
@@ -560,15 +827,225 @@ mergeInto(LibraryManager.library, {
SDL_CondWait: function() {},
SDL_DestroyCond: function() {},
-//SDL_CreateYUVOverlay
-//SDL_CreateThread, SDL_WaitThread etc
-
// SDL Mixer
- Mix_OpenAudio: function() { return -1 },
+ Mix_OpenAudio: function(frequency, format, channels, chunksize) {
+ return 0;
+ },
+
+ Mix_AllocateChannels: function(num) {
+ return num; // fake it
+ },
+
+ Mix_ChannelFinished: function(func) {
+ SDL.channelFinished = func; // TODO
+ },
+
+ Mix_HookMusicFinished: function(func) {
+ SDL.hookMusicFinished = func;
+ },
+
+ Mix_VolumeMusic: function(func) {
+ return 0; // TODO
+ },
+
+ Mix_LoadWAV_RW: function(filename, freesrc) {
+ filename = FS.standardizePath(Pointer_stringify(filename));
+ var raw = preloadedAudios[filename];
+ if (!raw) {
+ Module.printErr('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].audio.pause();
+ SDL.audios[id] = null;
+ },
+
+ Mix_PlayChannel: function(channel, id, loops) {
+ // TODO: handle loops
+ var audio = SDL.audios[id].audio;
+ if (!audio) return 0;
+ if (audio.currentTime) audio.src = audio.src; // This hack prevents lags on replaying // TODO: parallel sounds through //cloneNode(true).play()
+ audio.play();
+ return 1; // XXX should return channel
+ },
+ Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
+
+ 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();
+ },
+
+ 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);
@@ -577,6 +1054,9 @@ mergeInto(LibraryManager.library, {
SDL_RemoveTimer: function(id) {
window.clearTimeout(id);
return true;
- },
-});
+ }
+};
+
+autoAddDeps(LibrarySDL, '$SDL');
+mergeInto(LibraryManager.library, LibrarySDL);