aboutsummaryrefslogtreecommitdiff
path: root/src/library_sdl.js
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2012-05-24 10:13:42 +0200
committerAlon Zakai <alonzakai@gmail.com>2012-05-24 10:13:42 +0200
commita6d312ccb942b50287777fc22dc69de926ea3412 (patch)
tree0acabef2e78a97e7cd1742b9d98acb2a1b496e72 /src/library_sdl.js
parentc3570e254952ba0593038993674473e900ada9e0 (diff)
parentb7ce870dd4b1352e308e212e77cd6161c1ec904e (diff)
Merge branch 'master' into llvmsvn
Diffstat (limited to 'src/library_sdl.js')
-rw-r--r--src/library_sdl.js324
1 files changed, 199 insertions, 125 deletions
diff --git a/src/library_sdl.js b/src/library_sdl.js
index f71527af..e2e199e5 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1,79 +1,7 @@
//"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
- };
-
- // 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.
+// See browser tests for examples (tests/runner.py, search for sdl_). Run with
+// python tests/runner.py browser
var LibrarySDL = {
$SDL__deps: ['$FS', '$Browser'],
@@ -92,19 +20,17 @@ var LibrarySDL = {
fonts: [null],
keyboardState: null,
+ shiftKey: false,
+ ctrlKey: false,
+ altKey: false,
+
startTime: null,
mouseX: 0,
mouseY: 0,
- DOMEventToSDLEvent: {
- 'keydown': 0x300,
- 'keyup': 0x301,
- 'mousedown': 0x401,
- 'mouseup': 0x402,
- 'mousemove': 0x400
- },
+ DOMEventToSDLEvent: {},
- keyCodes: { // DOM code ==> SDL code
+ 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
@@ -116,10 +42,39 @@ var LibrarySDL = {
17: 305, // control (right, or left)
18: 308, // alt
109: 45, // minus
- 16: 304 // shift
+ 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
+ scanCodes: { // SDL keycode ==> SDL scancode. See SDL_scancode.h
97: 4, // A
98: 5,
99: 6,
@@ -146,25 +101,26 @@ var LibrarySDL = {
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
+ 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
- 47: 56, // slash
- 1106: 82, // up arrow
- 1105: 81, // down arrow
- 1104: 80, // left arrow
- 1103: 79 // right arrow
+ 305: 224, // ctrl
+ 308: 226, // alt
},
structs: {
@@ -296,10 +252,7 @@ var LibrarySDL = {
} else {
canvas = Module['canvas'];
}
- var ctx = Browser.createContext(canvas, useWebGL);
- if (usePageCanvas) {
- Module.ctx = ctx;
- }
+ var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas);
SDL.surfaces[surf] = {
width: width,
height: height,
@@ -326,8 +279,25 @@ var LibrarySDL = {
receiveEvent: function(event) {
switch(event.type) {
- case 'keydown': case 'keyup': case 'mousedown': case 'mouseup': case 'mousemove':
+ 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
@@ -335,7 +305,6 @@ var LibrarySDL = {
}
break;
}
- //event.preventDefault();
return false;
},
@@ -350,11 +319,18 @@ var LibrarySDL = {
case 'keydown': case 'keyup': {
var down = event.type === 'keydown';
//Module.print('Received key event: ' + event.keyCode);
- var key = SDL.keyCodes[event.keyCode] || 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;
+ }
+ var scan;
+ if (key >= 1024) {
+ scan = key - 1024;
+ } else {
+ scan = SDL.scanCodes[key] || key;
}
- 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') }}}
@@ -363,10 +339,14 @@ var LibrarySDL = {
{{{ 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': case 'mousemove': {
@@ -385,8 +365,8 @@ var LibrarySDL = {
{{{ 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') }}};
+ {{{ 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;
@@ -409,6 +389,20 @@ var LibrarySDL = {
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) {
@@ -439,6 +433,12 @@ var LibrarySDL = {
});
SDL.keyboardState = _malloc(0x10000);
_memset(SDL.keyboardState, 0, 0x10000);
+ // Initialize this structure carefully for closure
+ SDL.DOMEventToSDLEvent['keydown'] = 0x300;
+ SDL.DOMEventToSDLEvent['keyup'] = 0x301;
+ SDL.DOMEventToSDLEvent['mousedown'] = 0x401;
+ SDL.DOMEventToSDLEvent['mouseup'] = 0x402;
+ SDL.DOMEventToSDLEvent['mousemove'] = 0x400;
return 0; // success
},
@@ -466,7 +466,7 @@ var LibrarySDL = {
},
SDL_SetVideoMode: function(width, height, depth, flags) {
- ['mousedown', 'mouseup', 'mousemove'].forEach(function(event) {
+ ['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
Module['canvas'].width = width;
@@ -604,12 +604,28 @@ var LibrarySDL = {
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
},
@@ -731,16 +747,23 @@ var LibrarySDL = {
},
SDL_WM_GrabInput: function() {},
- SDL_ShowCursor: 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) {
- Module.printErr('Cannot find preloaded image ' + filename);
+ Runtime.warnOnce('Cannot find preloaded image ' + filename);
return 0;
}
var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
@@ -755,10 +778,13 @@ var LibrarySDL = {
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');
@@ -830,30 +856,38 @@ var LibrarySDL = {
// SDL Mixer
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
+ SDL.allocateChannels(32);
return 0;
},
+ Mix_CloseAudio: 'SDL_CloseAudio',
+
Mix_AllocateChannels: function(num) {
- return num; // fake it
+ SDL.allocateChannels(num);
+ return num;
},
Mix_ChannelFinished: function(func) {
- SDL.channelFinished = func; // TODO
+ SDL.channelFinished = func;
},
- Mix_HookMusicFinished: function(func) {
- SDL.hookMusicFinished = 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_VolumeMusic: function(func) {
- return 0; // TODO
+ Mix_SetPanning: function() {
+ return 0; // error
},
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);
+ Runtime.warnOnce('Cannot find preloaded audio ' + filename);
return 0;
}
var id = SDL.audios.length;
@@ -865,7 +899,6 @@ var LibrarySDL = {
},
Mix_FreeChunk: function(id) {
- SDL.audios[id].audio.pause();
SDL.audios[id] = null;
},
@@ -873,13 +906,54 @@ var LibrarySDL = {
// 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
+ 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) {