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.js1209
1 files changed, 1005 insertions, 204 deletions
diff --git a/src/library_sdl.js b/src/library_sdl.js
index cd86fb93..73848502 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1,124 +1,187 @@
//"use strict";
-// To use emscripten's SDL library here, you need to define
-// Module.canvas.
-//
-// More specifically, our SDL implementation will look for
-// Module.canvas. You should fill it using something like
-//
-// function onLoad() {
-// // Pass canvas and context to the generated code
-// Module.canvas = document.getElementById('canvas');
-// }
-//
-// Note that this must be called during onload, since you will
-// only be able to access the canvas element in the page after
-// it loads. You will likely also want to disable running by
-// default, with something like
-//
-// var Module = {
-// noInitialRun: true
-// };
-//
-// which is defined BEFORE you load the compiled code.
-
-// The test_emcc test in the tests/runner.py will test this
-// in its last phase, where it generates HTML. You can see
-// a concrete example there. The HTML source is in src/shell.html.
-// Here is a more comprehensive example:
-
-/*
-<html>
- <head>
- <title>Demo</title>
- <script type='text/javascript'>
- var Module = {
- noInitialRun: true
- };
+// See browser tests for examples (tests/runner.py, search for sdl_). Run with
+// python tests/runner.py browser
- // implement print
- var print = function(text) {
- var element = document.getElementById('output')
- element.innerHTML = text.replace('\n', '<br>', 'g') + element.innerHTML;
- }
- </script>
- <script src='doom.ccsimple.js' type='text/javascript'></script>
- <script type='text/javascript'>
- function onLoad() {
- // Pass canvas and context to the generated code, and do the actual run() here
- Module.canvas = document.getElementById('canvas');
- Module.run();
- }
- </script>
- <body onload='onLoad()' style='background-color: black; color: white'>
- <center>
- <canvas id='canvas' width='320' height='200'></canvas>
- </center>
- <div id='output'></div>
- </body>
-</html>
-*/
-
-// Other stuff to take into account:
-//
-// * Make sure alpha values are proper in your input. If they are all 0, everything will be transparent!
-//
-// * Your code should not write a 32-bit value and expect that to set an RGBA pixel.
-// The reason is that that data will be read as 8-bit values, and according to the
-// load-store consistency assumption, it should be written that way (see docs/paper.pdf).
-// Instead, do something like *ptr++ = R; *ptr++ = G; *ptr++ = B;
-//
-// * A normal C++ main loop with SDL_Delay will not work in JavaScript - there is no way
-// to wait for a short time without locking up the web page entirely. The simplest
-// solution here is to have a singleIteration() function which is a single loop
-// iteration, and from JS to do something like setInterval(_singleIteration, 1/30)
-//
-// * 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: [],
+ fonts: [null],
+
+ audios: [null],
+ music: {
+ audio: null,
+ volume: 1.0
+ },
+ mixerFrequency: 22050,
+ mixerFormat: 0x8010, // AUDIO_S16LSB
+ mixerNumChannels: 2,
+ mixerChunkSize: 1024,
+
+ keyboardState: null,
+ shiftKey: false,
+ ctrlKey: false,
+ altKey: false,
+
+ startTime: null,
+ mouseX: 0,
+ mouseY: 0,
+ buttonState: 0,
+ DOMButtons: [0, 0, 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
- 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
+ 173: 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([
- ['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,68 +192,143 @@ 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) + ')';
+ },
+
+ translateRGBAToColor: function(r, g, b, a) {
+ return (r << 24) + (g << 16) + (b << 8) + a;
+ },
+
+ 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
+ //surface with SDL_HWPALETTE flag is 8bpp surface (1 byte)
+ var is_SDL_HWPALETTE = flags & 0x00200000;
+ var bpp = is_SDL_HWPALETTE ? 1 : 4;
+
{{{ 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', 'i16') }}} // SDL_Surface.pitch, assuming RGBA for now,
+ {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*4', '0', 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed 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.BitsPerPixel', '0', 'bpp * 8', 'i8') }}}
+ {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BytesPerPixel', '0', 'bpp', 'i8') }}}
- {{{ 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, usePageCanvas);
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,
+
+ isFlagSet: function (flag) {
+ return flags & flag;
+ }
};
+
return surf;
},
- createContext: function(useWebGL) {
-#if !USE_TYPED_ARRAYS
- if (useWebGL) {
- Module.print('(USE_TYPED_ARRAYS needs to be enabled for WebGL)');
- return null;
+ // Copy data from the C++-accessible storage to the canvas backing
+ // for surface with HWPALETTE flag(8bpp depth)
+ copyIndexedColorData: function(surfData, rX, rY, rW, rH) {
+ // HWPALETTE works with palette
+ // setted by SDL_SetColors
+ if (!surfData.colors) {
+ return;
}
-#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";
+
+ var fullWidth = Module['canvas'].width;
+ var fullHeight = Module['canvas'].height;
+
+ var startX = rX || 0;
+ var startY = rY || 0;
+ var endX = (rW || (fullWidth - startX)) + startX;
+ var endY = (rH || (fullHeight - startY)) + startY;
+
+ var buffer = surfData.buffer;
+ var data = surfData.image.data;
+ var colors = surfData.colors;
+
+ for (var y = startY; y < endY; ++y) {
+ var indexBase = y * fullWidth;
+ var colorBase = indexBase * 4;
+ for (var x = startX; x < endX; ++x) {
+ // HWPALETTE have only 256 colors (not rgba)
+ var index = {{{ makeGetValue('buffer + indexBase + x', '0', 'i8', null, true) }}};
+ var color = colors[index] || [Math.floor(Math.random()*255),Math.floor(Math.random()*255),Math.floor(Math.random()*255)]; // XXX
+ var colorOffset = colorBase + x * 4;
+
+ data[colorOffset ] = color[0];
+ data[colorOffset +1] = color[1];
+ data[colorOffset +2] = color[2];
+ //unused: data[colorOffset +3] = color[3];
}
- return Module.ctx = ctx;
- } catch (e) {
- Module.print('(canvas not available)');
- return null;
}
},
@@ -198,61 +336,233 @@ mergeInto(LibraryManager.library, {
_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 '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;
}
- //event.preventDefault();
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__);
+ _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';
- var key = SDL.keyCodes[event.keyCode] || event.keyCode;
+ //Module.print('Received key event: ' + event.keyCode);
+ var key = event.keyCode;
if (key >= 65 && key <= 90) {
- key = String.fromCharCode(key).toLowerCase().charCodeAt(0);
+ key += 32; // make lowercase for SDL
+ } else {
+ key = SDL.keyCodes[event.keyCode] || event.keyCode;
}
- //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;
+ 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', '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('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': {
+ if (Browser.pointerLock) {
+ // When the pointer is locked, calculate the coordinates
+ // based on the movement of the mouse.
+ var movementX = Browser.getMovementX(event);
+ var movementY = Browser.getMovementY(event);
+ var x = SDL.mouseX + movementX;
+ var y = SDL.mouseY + movementY;
+ } else {
+ // Otherwise, calculate the movement based on the changes
+ // in the coordinates.
+ var x = event.pageX - Module["canvas"].offsetLeft;
+ var y = event.pageY - Module["canvas"].offsetTop;
+ var movementX = x - SDL.mouseX;
+ var movementY = y - SDL.mouseY;
+ }
+ 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', 'movementX', 'i32') }}};
+ {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'movementY', 'i32') }}};
+ }
+ SDL.mouseX = x;
+ SDL.mouseY = y;
break;
- case 'keypress': break // TODO
- default:
- throw 'Unhandled SDL event: ' + event.type;
+ }
+ 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
+ };
+ }
+ },
+
+ setGetVolume: function(info, volume) {
+ if (!info) return 0;
+ var ret = info.volume * 128; // MIX_MAX_VOLUME
+ if (volume != -1) {
+ info.volume = volume / 128;
+ if (info.audio) info.audio.volume = info.volume;
+ }
+ 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_Init__deps: ['$SDL'],
+ 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', 'keypress'].forEach(function(event) {
+ ['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: 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,61 +579,146 @@ mergeInto(LibraryManager.library, {
return -1; // -1 == all modes are ok. TODO
},
- SDL_GL_SetAttribute: function(attr, value) {
- // TODO
+ SDL_VideoModeOK: function(width, height, depth, flags) {
+ // SDL_VideoModeOK returns 0 if the requested mode is not supported under any bit depth, or returns the
+ // bits-per-pixel of the closest available mode with the given width, height and requested surface flags
+ return depth; // all modes are ok.
+ },
+
+ SDL_VideoDriverName: function(buf, max_size) {
+ if (SDL.startTime === null) {
+ return 0; //return NULL
+ }
+ //driverName - emscripten_sdl_driver
+ var driverName = [101, 109, 115, 99, 114, 105, 112, 116, 101,
+ 110, 95, 115, 100, 108, 95, 100, 114, 105, 118, 101, 114];
+
+ var index = 0;
+ var size = driverName.length;
+
+ if (max_size <= size) {
+ size = max_size - 1; //-1 cause null-terminator
+ }
+
+ while (index < size) {
+ var value = driverName[index];
+ {{{ makeSetValue('buf', 'index', 'value', 'i8') }}};
+ index++;
+ }
+
+ {{{ makeSetValue('buf', 'index', '0', 'i8') }}};
+ return buf;
},
SDL_SetVideoMode: function(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);
+ return SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
+ },
+
+ SDL_GetVideoSurface: function() {
+ return SDL.screen;
+ },
+
+ SDL_QuitSubSystem: function(flags) {
+ Module.print('SDL_QuitSubSystem called (and ignored)');
},
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);
- 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 (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) {
+ // If this is neaded then
+ // we should compact the data from 32bpp to 8bpp index.
+ // I think best way to implement this is use
+ // additional colorMap hash (color->index).
+ // Something like this:
+ //
+ // var size = surfData.width * surfData.height;
+ // var data = '';
+ // for (var i = 0; i<size; i++) {
+ // var color = SDL.translateRGBAToColor(
+ // surfData.image.data[i*4 ],
+ // surfData.image.data[i*4 +1],
+ // surfData.image.data[i*4 +2],
+ // 255);
+ // var index = surfData.colorMap[color];
+ // {{{ makeSetValue('surfData.buffer', 'i', 'index', 'i8') }}};
+ // }
+ throw 'CopyOnLock is not supported for SDL_LockSurface with SDL_HWPALETTE flag set' + new Error().stack;
+ } else {
+#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) {
+ if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) {
+ SDL.copyIndexedColorData(surfData);
+ } else if (!surfData.colors) {
+ var num = surfData.image.data.length;
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 ] = 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;
}
@@ -331,7 +726,7 @@ mergeInto(LibraryManager.library, {
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' : '') }}};
+ data[i] = {{{ makeGetValue('buffer', 'i', 'i8', null, true) }}};
if (i % 4 == 3) data[i] = 0xff;
}
#endif
@@ -345,7 +740,7 @@ mergeInto(LibraryManager.library, {
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 val = {{{ makeGetValue('s++', '0', 'i8', null, true) }}};
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];
@@ -361,15 +756,20 @@ 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) {
// We actually do the whole screen in Unlock...
},
+ SDL_UpdateRects: function(surf, numrects, rects) {
+ // We actually do the whole screen in Unlock...
+ },
+
SDL_Delay: function(delay) {
- Module.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 +781,37 @@ mergeInto(LibraryManager.library, {
// TODO
},
+ SDL_GetKeyboardState: function() {
+ return SDL.keyboardState;
+ },
+
+ SDL_GetKeyState__deps: ['SDL_GetKeyboardState'],
+ SDL_GetKeyState: function() {
+ return _SDL_GetKeyboardState();
+ },
+
+ 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
},
@@ -390,55 +821,74 @@ 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