3
// See browser tests for examples (tests/runner.py, search for sdl_). Run with
4
// python tests/runner.py browser
7
// SDL_VIDEORESIZE: This is sent when the canvas is resized. Note that the user
8
// cannot manually do so, so this is only sent when the
9
// program manually resizes it (emscripten_set_canvas_size
13
$SDL__deps: ['$FS', '$Browser'],
27
// The currently preloaded audio elements ready to be played
29
// The currently playing audio element. There's only one music track.
34
mixerFrequency: 22050,
35
mixerFormat: 0x8010, // AUDIO_S16LSB
38
channelMinimumNumber: 0,
40
GL: false, // Set to true if we call SDL_SetVideoMode with SDL_OPENGL, and if so, we do not create 2D canvases&contexts for blitting
41
// Note that images loaded before SDL_SetVideoMode will not get this optimization
52
DOMButtons: [0, 0, 0],
54
DOMEventToSDLEvent: {},
56
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
57
46: 127, // SDLK_DEL == '\177'
59
40: 1105, // down arrow
60
37: 1104, // left arrow
61
39: 1103, // right arrow
66
17: 1248, // control (right, or left)
71
96: 88 | 1<<10, // keypad 0
72
97: 89 | 1<<10, // keypad 1
73
98: 90 | 1<<10, // keypad 2
74
99: 91 | 1<<10, // keypad 3
75
100: 92 | 1<<10, // keypad 4
76
101: 93 | 1<<10, // keypad 5
77
102: 94 | 1<<10, // keypad 6
78
103: 95 | 1<<10, // keypad 7
79
104: 96 | 1<<10, // keypad 8
80
105: 97 | 1<<10, // keypad 9
82
112: 58 | 1<<10, // F1
83
113: 59 | 1<<10, // F2
84
114: 60 | 1<<10, // F3
85
115: 61 | 1<<10, // F4
86
116: 62 | 1<<10, // F5
87
117: 63 | 1<<10, // F6
88
118: 64 | 1<<10, // F7
89
119: 65 | 1<<10, // F8
90
120: 66 | 1<<10, // F9
91
121: 67 | 1<<10, // F10
92
122: 68 | 1<<10, // F11
93
123: 69 | 1<<10, // F12
98
192: 96, // backtick/backquote (`)
101
scanCodes: { // SDL keycode ==> SDL scancode. See SDL_scancode.h
151
Rect: Runtime.generateStructInfo([
152
['i32', 'x'], ['i32', 'y'], ['i32', 'w'], ['i32', 'h'],
154
PixelFormat: Runtime.generateStructInfo([
156
['void*', 'palette'], ['i8', 'BitsPerPixel'], ['i8', 'BytesPerPixel'],
157
['i8', 'padding1'], ['i8', 'padding2'],
158
['i32', 'Rmask'], ['i32', 'Gmask'], ['i32', 'Bmask'], ['i32', 'Amask'],
159
['i8', 'Rloss'], ['i8', 'Gloss'], ['i8', 'Bloss'], ['i8', 'Aloss'],
160
['i8', 'Rshift'], ['i8', 'Gshift'], ['i8', 'Bshift'], ['i8', 'Ashift']
162
KeyboardEvent: Runtime.generateStructInfo([
171
keysym: Runtime.generateStructInfo([
177
TextInputEvent: Runtime.generateStructInfo([
182
MouseMotionEvent: Runtime.generateStructInfo([
194
MouseButtonEvent: Runtime.generateStructInfo([
204
ResizeEvent: Runtime.generateStructInfo([
209
AudioSpec: Runtime.generateStructInfo([
216
['void*', 'callback'],
217
['void*', 'userdata']
219
version: Runtime.generateStructInfo([
226
loadRect: function(rect) {
228
x: {{{ makeGetValue('rect + SDL.structs.Rect.x', '0', 'i32') }}},
229
y: {{{ makeGetValue('rect + SDL.structs.Rect.y', '0', 'i32') }}},
230
w: {{{ makeGetValue('rect + SDL.structs.Rect.w', '0', 'i32') }}},
231
h: {{{ makeGetValue('rect + SDL.structs.Rect.h', '0', 'i32') }}}
235
// Load SDL color into a CSS-style color specification
236
loadColorToCSSRGB: function(color) {
237
var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
238
return 'rgb(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ')';
240
loadColorToCSSRGBA: function(color) {
241
var rgba = {{{ makeGetValue('color', '0', 'i32') }}};
242
return 'rgba(' + (rgba&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba >> 16)&255) + ',' + (((rgba >> 24)&255)/255) + ')';
245
translateColorToCSSRGBA: function(rgba) {
246
return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
249
translateRGBAToCSSRGBA: function(r, g, b, a) {
250
return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
253
translateRGBAToColor: function(r, g, b, a) {
254
return (r << 24) + (g << 16) + (b << 8) + a;
257
makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) {
259
var surf = _malloc(14*Runtime.QUANTUM_SIZE); // SDL_Surface has 14 fields of quantum size
260
var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time
261
var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE);
262
flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked
264
//surface with SDL_HWPALETTE flag is 8bpp surface (1 byte)
265
var is_SDL_HWPALETTE = flags & 0x00200000;
266
var bpp = is_SDL_HWPALETTE ? 1 : 4;
268
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} // SDL_Surface.flags
269
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*1', '0', 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO
270
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*2', '0', 'width', 'i32') }}} // SDL_Surface.w
271
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*3', '0', 'height', 'i32') }}} // SDL_Surface.h
272
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*4', '0', 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now,
273
// since that is what ImageData gives us in browsers
274
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*5', '0', 'buffer', 'void*') }}} // SDL_Surface.pixels
275
{{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*6', '0', '0', 'i32*') }}} // SDL_Surface.offset
277
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.format', '0', '-2042224636', 'i32') }}} // SDL_PIXELFORMAT_RGBA8888
278
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.palette', '0', '0', 'i32') }}} // TODO
279
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', 'bpp * 8', 'i8') }}}
280
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BytesPerPixel', '0', 'bpp', 'i8') }}}
282
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Rmask', '0', 'rmask || 0x000000ff', 'i32') }}}
283
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Gmask', '0', 'gmask || 0x0000ff00', 'i32') }}}
284
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Bmask', '0', 'bmask || 0x00ff0000', 'i32') }}}
285
{{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Amask', '0', 'amask || 0xff000000', 'i32') }}}
287
// Decide if we want to use WebGL or not
288
var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL
289
SDL.GL = SDL.GL || useWebGL;
291
if (!usePageCanvas) {
292
canvas = document.createElement('canvas');
293
canvas.width = width;
294
canvas.height = height;
296
canvas = Module['canvas'];
298
var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas);
299
SDL.surfaces[surf] = {
306
pixelFormat: pixelFormat,
310
usePageCanvas: usePageCanvas,
313
isFlagSet: function (flag) {
321
// Copy data from the C++-accessible storage to the canvas backing
322
// for surface with HWPALETTE flag(8bpp depth)
323
copyIndexedColorData: function(surfData, rX, rY, rW, rH) {
324
// HWPALETTE works with palette
325
// setted by SDL_SetColors
326
if (!surfData.colors) {
330
var fullWidth = Module['canvas'].width;
331
var fullHeight = Module['canvas'].height;
333
var startX = rX || 0;
334
var startY = rY || 0;
335
var endX = (rW || (fullWidth - startX)) + startX;
336
var endY = (rH || (fullHeight - startY)) + startY;
338
var buffer = surfData.buffer;
339
var data = surfData.image.data;
340
var colors = surfData.colors;
342
for (var y = startY; y < endY; ++y) {
343
var indexBase = y * fullWidth;
344
var colorBase = indexBase * 4;
345
for (var x = startX; x < endX; ++x) {
346
// HWPALETTE have only 256 colors (not rgba)
347
var index = {{{ makeGetValue('buffer + indexBase + x', '0', 'i8', null, true) }}} * 3;
348
var colorOffset = colorBase + x * 4;
350
data[colorOffset ] = colors[index ];
351
data[colorOffset +1] = colors[index +1];
352
data[colorOffset +2] = colors[index +2];
353
//unused: data[colorOffset +3] = color[index +3];
358
freeSurface: function(surf) {
359
_free(SDL.surfaces[surf].buffer);
360
_free(SDL.surfaces[surf].pixelFormat);
362
SDL.surfaces[surf] = null;
365
receiveEvent: function(event) {
368
if (Browser.pointerLock) {
369
// workaround for firefox bug 750111
370
if ('mozMovementX' in event) {
371
event['movementX'] = event['mozMovementX'];
372
event['movementY'] = event['mozMovementY'];
374
// workaround for Firefox bug 782777
375
if (event['movementX'] == 0 && event['movementY'] == 0) {
376
// ignore a mousemove event if it doesn't contain any movement info
377
// (without pointer lock, we infer movement from pageX/pageY, so this check is unnecessary)
382
case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
383
if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') {
384
var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3;
391
SDL.events.push(event2);
398
} else if (event.type == 'mousedown') {
399
SDL.DOMButtons[event.button] = 1;
400
} else if (event.type == 'mouseup') {
401
if (!SDL.DOMButtons[event.button]) return false; // ignore extra ups, can happen if we leave the canvas while pressing down, then return,
402
// since we add a mouseup in that case
403
SDL.DOMButtons[event.button] = 0;
406
if (event.type == 'keypress' && !SDL.textInput) {
410
SDL.events.push(event);
413
// Un-press all pressed mouse buttons, because we might miss the release outside of the canvas
414
for (var i = 0; i < 3; i++) {
415
if (SDL.DOMButtons[i]) {
422
SDL.DOMButtons[i] = 0;
427
case 'visibilitychange': {
428
// Un-press all pressed keys: TODO
429
for (var code in SDL.keyboardMap) {
432
keyCode: SDL.keyboardMap[code]
438
if (Browser.mainLoop.runner) {
439
SDL.events.push(event);
440
// Force-run a main event loop, since otherwise this event will never be caught!
441
Browser.mainLoop.runner();
445
SDL.events.push(event);
448
if (SDL.events.length >= 10000) {
449
Module.printErr('SDL event queue full, dropping events');
450
SDL.events = SDL.events.slice(0, 10000);
455
makeCEvent: function(event, ptr) {
456
if (typeof event === 'number') {
457
// This is a pointer to a native C event that was SDL_PushEvent'ed
458
_memcpy(ptr, event, SDL.structs.KeyboardEvent.__size__); // XXX
463
case 'keydown': case 'keyup': {
464
var down = event.type === 'keydown';
465
//Module.print('Received key event: ' + event.keyCode);
466
var key = event.keyCode;
467
if (key >= 65 && key <= 90) {
468
key += 32; // make lowercase for SDL
470
key = SDL.keyCodes[event.keyCode] || event.keyCode;
476
scan = SDL.scanCodes[key] || key;
478
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}
479
//{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.which', '1', 'i32') }}}
480
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}}
481
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO
483
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}}
484
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}}
485
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', '0', 'i32') }}}
486
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'key', 'i32') }}}
488
var code = SDL.keyCodes[event.keyCode] || event.keyCode;
489
{{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}};
491
SDL.keyboardMap[code] = event.keyCode; // save the DOM input, which we can use to unpress it during blur
493
delete SDL.keyboardMap[code];
499
{{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}
500
// Not filling in windowID for now
501
var cStr = intArrayFromString(String.fromCharCode(event.charCode));
502
for (var i = 0; i < cStr.length; ++i) {
503
{{{ makeSetValue('ptr', 'SDL.structs.TextInputEvent.text + i', 'cStr[i]', 'i8') }}};
507
case 'mousedown': case 'mouseup':
508
if (event.type == 'mousedown') {
509
// SDL_BUTTON(x) is defined as (1 << ((x)-1)). SDL buttons are 1-3,
510
// and DOM buttons are 0-2, so this means that the below formula is
512
SDL.buttonState |= 1 << event.button;
513
} else if (event.type == 'mouseup') {
514
SDL.buttonState &= ~(1 << event.button);
518
if (Browser.pointerLock) {
519
// When the pointer is locked, calculate the coordinates
520
// based on the movement of the mouse.
521
// Workaround for Firefox bug 764498
522
if (event.type != 'mousemove' &&
523
('mozMovementX' in event)) {
524
var movementX = 0, movementY = 0;
526
var movementX = Browser.getMovementX(event);
527
var movementY = Browser.getMovementY(event);
529
var x = SDL.mouseX + movementX;
530
var y = SDL.mouseY + movementY;
532
// Otherwise, calculate the movement based on the changes
533
// in the coordinates.
534
var rect = Module["canvas"].getBoundingClientRect();
535
var x = event.pageX - (window.scrollX + rect.left);
536
var y = event.pageY - (window.scrollY + rect.top);
538
// the canvas might be CSS-scaled compared to its backbuffer;
539
// SDL-using content will want mouse coordinates in terms
540
// of backbuffer units.
541
var cw = Module["canvas"].width;
542
var ch = Module["canvas"].height;
543
x = x * (cw / rect.width);
544
y = y * (ch / rect.height);
546
var movementX = x - SDL.mouseX;
547
var movementY = y - SDL.mouseY;
549
if (event.type != 'mousemove') {
550
var down = event.type === 'mousedown';
551
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
552
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.button', 'event.button+1', 'i8') }}}; // DOM buttons are 0-2, SDL 1-3
553
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.state', 'down ? 1 : 0', 'i8') }}};
554
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.x', 'x', 'i32') }}};
555
{{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.y', 'y', 'i32') }}};
557
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
558
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.state', 'SDL.buttonState', 'i8') }}};
559
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.x', 'x', 'i32') }}};
560
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.y', 'y', 'i32') }}};
561
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.xrel', 'movementX', 'i32') }}};
562
{{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'movementY', 'i32') }}};
569
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
573
{{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
574
{{{ makeSetValue('ptr', 'SDL.structs.ResizeEvent.w', 'event.w', 'i32') }}};
575
{{{ makeSetValue('ptr', 'SDL.structs.ResizeEvent.h', 'event.h', 'i32') }}};
578
default: throw 'Unhandled SDL event: ' + event.type;
582
estimateTextWidth: function(fontData, text) {
583
var h = fontData.size;
584
var fontString = h + 'px sans-serif';
585
// TODO: use temp context, not screen's, to avoid affecting its performance?
586
var tempCtx = SDL.surfaces[SDL.screen].ctx;
588
tempCtx.font = fontString;
589
var ret = tempCtx.measureText(text).width | 0;
596
// Channels are a SDL abstraction for allowing multiple sound tracks to be
597
// played at the same time. We don't need to actually implement the mixing
598
// since the browser engine handles that for us. Therefore, in JS we just
599
// maintain a list of channels and return IDs for them to the SDL consumer.
600
allocateChannels: function(num) { // called from Mix_AllocateChannels and init
601
if (SDL.numChannels && SDL.numChannels >= num) return;
602
SDL.numChannels = num;
604
for (var i = 0; i < num; i++) {
612
setGetVolume: function(info, volume) {
614
var ret = info.volume * 128; // MIX_MAX_VOLUME
616
info.volume = volume / 128;
617
if (info.audio) info.audio.volume = info.volume;
624
debugSurface: function(surfData) {
625
console.log('dumping surface ' + [surfData.surf, surfData.source, surfData.width, surfData.height]);
626
var image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
627
var data = image.data;
628
var num = Math.min(surfData.width, surfData.height);
629
for (var i = 0; i < num; i++) {
630
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]]);
635
SDL_Linked_Version: function() {
636
if (SDL.version === null) {
637
SDL.version = _malloc(SDL.structs.version.__size__);
638
{{{ makeSetValue('SDL.version + SDL.structs.version.major', '0', '1', 'i8') }}}
639
{{{ makeSetValue('SDL.version + SDL.structs.version.minor', '0', '3', 'i8') }}}
640
{{{ makeSetValue('SDL.version + SDL.structs.version.patch', '0', '0', 'i8') }}}
645
SDL_Init: function(what) {
646
SDL.startTime = Date.now();
647
// capture all key events. we just keep down and up, but also capture press to prevent default actions
648
if (!Module['doNotCaptureKeyboard']) {
649
document.onkeydown = SDL.receiveEvent;
650
document.onkeyup = SDL.receiveEvent;
651
document.onkeypress = SDL.receiveEvent;
652
document.onblur = SDL.receiveEvent;
653
document.addEventListener("visibilitychange", SDL.receiveEvent);
655
window.onunload = SDL.receiveEvent;
656
SDL.keyboardState = _malloc(0x10000); // Our SDL needs 512, but 64K is safe for older SDLs
657
_memset(SDL.keyboardState, 0, 0x10000);
658
// Initialize this structure carefully for closure
659
SDL.DOMEventToSDLEvent['keydown'] = 0x300 /* SDL_KEYDOWN */;
660
SDL.DOMEventToSDLEvent['keyup'] = 0x301 /* SDL_KEYUP */;
661
SDL.DOMEventToSDLEvent['keypress'] = 0x303 /* SDL_TEXTINPUT */;
662
SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
663
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
664
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
665
SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
666
SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
670
SDL_WasInit__deps: ['SDL_Init'],
671
SDL_WasInit: function() {
672
if (SDL.startTime === null) {
678
SDL_GetVideoInfo: function() {
679
// %struct.SDL_VideoInfo = type { i32, i32, %struct.SDL_PixelFormat*, i32, i32 } - 5 fields of quantum size
680
var ret = _malloc(5*Runtime.QUANTUM_SIZE);
681
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO
682
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO
683
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}}
684
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}}
685
{{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}}
689
SDL_ListModes: function(format, flags) {
690
return -1; // -1 == all modes are ok. TODO
693
SDL_VideoModeOK: function(width, height, depth, flags) {
694
// SDL_VideoModeOK returns 0 if the requested mode is not supported under any bit depth, or returns the
695
// bits-per-pixel of the closest available mode with the given width, height and requested surface flags
696
return depth; // all modes are ok.
699
SDL_VideoDriverName: function(buf, max_size) {
700
if (SDL.startTime === null) {
701
return 0; //return NULL
703
//driverName - emscripten_sdl_driver
704
var driverName = [101, 109, 115, 99, 114, 105, 112, 116, 101,
705
110, 95, 115, 100, 108, 95, 100, 114, 105, 118, 101, 114];
708
var size = driverName.length;
710
if (max_size <= size) {
711
size = max_size - 1; //-1 cause null-terminator
714
while (index < size) {
715
var value = driverName[index];
716
{{{ makeSetValue('buf', 'index', 'value', 'i8') }}};
720
{{{ makeSetValue('buf', 'index', '0', 'i8') }}};
724
SDL_SetVideoMode: function(width, height, depth, flags) {
725
['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
726
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
728
Browser.setCanvasSize(width, height, true);
729
SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
730
if (!SDL.addedResizeListener) {
731
SDL.addedResizeListener = true;
732
Browser.resizeListeners.push(function(w, h) {
743
SDL_GetVideoSurface: function() {
747
SDL_QuitSubSystem: function(flags) {
748
Module.print('SDL_QuitSubSystem called (and ignored)');
751
SDL_Quit: function() {
752
for (var i = 0; i < SDL.numChannels; ++i) {
753
if (SDL.channels[i].audio) {
754
SDL.channels[i].audio.pause();
757
if (SDL.music.audio) {
758
SDL.music.audio.pause();
760
Module.print('SDL_Quit called (and ignored)');
763
// Copy data from the canvas backing to a C++-accessible storage
764
SDL_LockSurface: function(surf) {
765
var surfData = SDL.surfaces[surf];
768
if (surfData.locked > 1) return 0;
770
surfData.image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
771
if (surf == SDL.screen) {
772
var data = surfData.image.data;
773
var num = data.length;
774
for (var i = 0; i < num/4; i++) {
775
data[i*4+3] = 255; // opacity, as canvases blend alpha
779
if (SDL.defaults.copyOnLock) {
780
// Copy pixel data to somewhere accessible to 'C/C++'
781
if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) {
782
// If this is neaded then
783
// we should compact the data from 32bpp to 8bpp index.
784
// I think best way to implement this is use
785
// additional colorMap hash (color->index).
786
// Something like this:
788
// var size = surfData.width * surfData.height;
790
// for (var i = 0; i<size; i++) {
791
// var color = SDL.translateRGBAToColor(
792
// surfData.image.data[i*4 ],
793
// surfData.image.data[i*4 +1],
794
// surfData.image.data[i*4 +2],
796
// var index = surfData.colorMap[color];
797
// {{{ makeSetValue('surfData.buffer', 'i', 'index', 'i8') }}};
799
throw 'CopyOnLock is not supported for SDL_LockSurface with SDL_HWPALETTE flag set' + new Error().stack;
801
#if USE_TYPED_ARRAYS == 2
802
HEAPU8.set(surfData.image.data, surfData.buffer);
804
var num2 = surfData.image.data.length;
805
for (var i = 0; i < num2; i++) {
806
{{{ makeSetValue('surfData.buffer', 'i', 'surfData.image.data[i]', 'i8') }}};
812
// Mark in C/C++-accessible SDL structure
813
// SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ...
814
// So we have fields all of the same size, and 5 of them before us.
815
// TODO: Use macros like in library.js
816
{{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}};
821
// Copy data from the C++-accessible storage to the canvas backing
822
SDL_UnlockSurface: function(surf) {
823
assert(!SDL.GL); // in GL mode we do not keep around 2D canvases and contexts
825
var surfData = SDL.surfaces[surf];
828
if (surfData.locked > 0) return;
830
// Copy pixel data to image
831
if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) {
832
SDL.copyIndexedColorData(surfData);
833
} else if (!surfData.colors) {
834
var num = surfData.image.data.length;
835
var data = surfData.image.data;
836
var buffer = surfData.buffer;
837
#if USE_TYPED_ARRAYS == 2
838
assert(buffer % 4 == 0, 'Invalid buffer offset: ' + buffer);
839
var src = buffer >> 2;
841
var isScreen = surf == SDL.screen;
843
// TODO: access underlying data buffer and write in 32-bit chunks or more
844
var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
845
data[dst ] = val & 0xff;
846
data[dst+1] = (val >> 8) & 0xff;
847
data[dst+2] = (val >> 16) & 0xff;
848
data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
853
for (var i = 0; i < num; i++) {
854
// We may need to correct signs here. Potentially you can hardcode a write of 255 to alpha, say, and
855
// the compiler may decide to write -1 in the llvm bitcode...
856
data[i] = {{{ makeGetValue('buffer', 'i', 'i8', null, true) }}};
857
if (i % 4 == 3) data[i] = 0xff;
861
var width = Module['canvas'].width;
862
var height = Module['canvas'].height;
863
var s = surfData.buffer;
864
var data = surfData.image.data;
865
var colors = surfData.colors;
866
for (var y = 0; y < height; y++) {
867
var base = y*width*4;
868
for (var x = 0; x < width; x++) {
869
// See comment above about signs
870
var val = {{{ makeGetValue('s++', '0', 'i8', null, true) }}} * 3;
871
var start = base + x*4;
872
data[start] = colors[val];
873
data[start+1] = colors[val+1];
874
data[start+2] = colors[val+2];
880
surfData.ctx.putImageData(surfData.image, 0, 0);
881
// Note that we save the image, so future writes are fast. But, memory is not yet released
884
SDL_Flip: function(surf) {
885
// We actually do this in Unlock, since the screen surface has as its canvas
886
// backing the page canvas element
889
SDL_UpdateRect: function(surf, x, y, w, h) {
890
// We actually do the whole screen in Unlock...
893
SDL_UpdateRects: function(surf, numrects, rects) {
894
// We actually do the whole screen in Unlock...
897
SDL_Delay: function(delay) {
898
throw 'SDL_Delay called! Potential infinite loop, quitting. ' + new Error().stack;
901
SDL_WM_SetCaption: function(title, icon) {
902
title = title && Pointer_stringify(title);
903
icon = icon && Pointer_stringify(icon);
906
SDL_EnableKeyRepeat: function(delay, interval) {
910
SDL_GetKeyboardState: function() {
911
return SDL.keyboardState;
914
SDL_GetKeyState__deps: ['SDL_GetKeyboardState'],
915
SDL_GetKeyState: function() {
916
return _SDL_GetKeyboardState();
919
SDL_GetModState: function() {
920
// TODO: numlock, capslock, etc.
921
return (SDL.keyboardState[16] ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT
922
(SDL.keyboardState[17] ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL
923
(SDL.keyboardState[18] ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT
926
SDL_GetMouseState: function(x, y) {
927
if (x) {{{ makeSetValue('x', '0', 'SDL.mouseX', 'i32') }}};
928
if (y) {{{ makeSetValue('y', '0', 'SDL.mouseY', 'i32') }}};
929
return SDL.buttonState;
932
SDL_WarpMouse: function(x, y) {
933
return; // TODO: implement this in a non-buggy way. Need to keep relative mouse movements correct after calling this
934
var rect = Module["canvas"].getBoundingClientRect();
937
pageX: x + (window.scrollX + rect.left),
938
pageY: y + (window.scrollY + rect.top)
942
SDL_ShowCursor: function(toggle) {
944
case 0: // SDL_DISABLE
945
if (Browser.isFullScreen) { // only try to lock the pointer when in full screen mode
946
Module['canvas'].requestPointerLock();
948
} else { // else return SDL_ENABLE to indicate the failure
952
case 1: // SDL_ENABLE
953
Module['canvas'].exitPointerLock();
956
case -1: // SDL_QUERY
957
return !Browser.pointerLock;
960
console.log( "SDL_ShowCursor called with unknown toggle parameter value: " + toggle + "." );
965
SDL_GetError: function() {
966
return allocate(intArrayFromString("unknown SDL-emscripten error"), 'i8');
969
SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) {
970
return SDL.makeSurface(width, height, flags, false, 'CreateRGBSurface', rmask, gmask, bmask, amask);
973
SDL_DisplayFormatAlpha: function(surf) {
974
var oldData = SDL.surfaces[surf];
975
var ret = SDL.makeSurface(oldData.width, oldData.height, oldData.flags, false, 'copy:' + oldData.source);
976
var newData = SDL.surfaces[ret];
977
//newData.ctx.putImageData(oldData.ctx.getImageData(0, 0, oldData.width, oldData.height), 0, 0);
978
newData.ctx.drawImage(oldData.canvas, 0, 0);
982
SDL_FreeSurface: function(surf) {
983
if (surf) SDL.freeSurface(surf);
986
SDL_UpperBlit: function(src, srcrect, dst, dstrect) {
987
var srcData = SDL.surfaces[src];
988
var dstData = SDL.surfaces[dst];
991
sr = SDL.loadRect(srcrect);
993
sr = { x: 0, y: 0, w: srcData.width, h: srcData.height };
996
dr = SDL.loadRect(dstrect);
998
dr = { x: 0, y: 0, w: -1, h: -1 };
1000
dstData.ctx.drawImage(srcData.canvas, sr.x, sr.y, sr.w, sr.h, dr.x, dr.y, sr.w, sr.h);
1001
if (dst != SDL.screen) {
1002
// XXX As in IMG_Load, for compatibility we write out |pixels|
1003
console.log('WARNING: copying canvas data to memory for compatibility');
1004
_SDL_LockSurface(dst);
1005
dstData.locked--; // The surface is not actually locked in this hack
1010
SDL_FillRect: function(surf, rect, color) {
1011
var surfData = SDL.surfaces[surf];
1012
assert(!surfData.locked); // but we could unlock and re-lock if we must..
1014
if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) {
1015
//in SDL_HWPALETTE color is index (0..255)
1016
//so we should translate 1 byte value to
1018
var index = color * 3;
1019
color = SDL.translateRGBAToColor(surfData.colors[index], surfData.colors[index +1], surfData.colors[index +2], 255);
1022
var r = rect ? SDL.loadRect(rect) : { x: 0, y: 0, w: surfData.width, h: surfData.height };
1023
surfData.ctx.save();
1024
surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color);
1025
surfData.ctx.fillRect(r.x, r.y, r.w, r.h);
1026
surfData.ctx.restore();
1029
SDL_BlitSurface__deps: ['SDL_UpperBlit'],
1030
SDL_BlitSurface: function(src, srcrect, dst, dstrect) {
1031
return _SDL_UpperBlit(src, srcrect, dst, dstrect);
1034
zoomSurface: function(src, x, y, smooth) {
1035
var srcData = SDL.surfaces[src];
1036
var w = srcData.width*x;
1037
var h = srcData.height*y;
1038
var ret = SDL.makeSurface(w, h, srcData.flags, false, 'zoomSurface');
1039
var dstData = SDL.surfaces[ret];
1040
dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
1044
rotozoomSurface: function(src, angle, zoom, smooth) {
1045
var srcData = SDL.surfaces[src];
1046
var w = srcData.width * zoom;
1047
var h = srcData.height * zoom;
1048
var diagonal = Math.ceil(Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2)));
1049
var ret = SDL.makeSurface(diagonal, diagonal, srcData.flags, false, 'rotozoomSurface');
1050
var dstData = SDL.surfaces[ret];
1051
dstData.ctx.translate(diagonal / 2, diagonal / 2);
1052
dstData.ctx.rotate(angle * Math.PI / 180);
1053
dstData.ctx.drawImage(srcData.canvas, -w / 2, -h / 2, w, h);
1057
SDL_SetAlpha: function(surf, flag, alpha) {
1058
SDL.surfaces[surf].alpha = alpha;
1061
SDL_GetTicks: function() {
1062
return Math.floor(Date.now() - SDL.startTime);
1065
SDL_PollEvent: function(ptr) {
1066
if (SDL.events.length === 0) return 0;
1068
SDL.makeCEvent(SDL.events.shift(), ptr);
1073
SDL_PushEvent: function(ptr) {
1074
SDL.events.push(ptr); // XXX Should we copy it? Not clear from API
1078
SDL_PeepEvents: function(events, numEvents, action, from, to) {
1080
case 2: { // SDL_GETEVENT
1081
assert(numEvents == 1);
1083
while (SDL.events.length > 0 && numEvents > 0) {
1084
var type = SDL.DOMEventToSDLEvent[SDL.events[0].type];
1085
if (type < from || type > to) break;
1086
SDL.makeCEvent(SDL.events.shift(), events);
1089
// events += sizeof(..)
1093
default: throw 'SDL_PeepEvents does not yet support that action: ' + action;
1097
SDL_PumpEvents: function(){},
1099
SDL_SetColors: function(surf, colors, firstColor, nColors) {
1100
var surfData = SDL.surfaces[surf];
1102
// we should create colors array
1103
// only once cause client code
1104
// often wants to change portion
1105
// of palette not all palette.
1106
if (!surfData.colors) {
1107
surfData.colors = new Uint8Array(256 * 3); //256 RGB colors
1110
for (var i = firstColor; i < firstColor + nColors; i++) {
1112
surfData.colors[index] = {{{ makeGetValue('colors', 'i*4', 'i8', null, true) }}};
1113
surfData.colors[index +1] = {{{ makeGetValue('colors', 'i*4 +1', 'i8', null, true) }}};
1114
surfData.colors[index +2] = {{{ makeGetValue('colors', 'i*4 +2', 'i8', null, true) }}};
1120
SDL_MapRGB: function(fmt, r, g, b) {
1121
// Canvas screens are always RGBA
1122
return 0xff+((b&0xff)<<8)+((g&0xff)<<16)+((r&0xff)<<24)
1125
SDL_MapRGBA: function(fmt, r, g, b, a) {
1126
// Canvas screens are always RGBA
1127
return (a&0xff)+((b&0xff)<<8)+((g&0xff)<<16)+((r&0xff)<<24)
1130
SDL_WM_GrabInput: function() {},
1132
SDL_WM_ToggleFullScreen: function(surf) {
1133
if (Browser.isFullScreen) {
1134
Module['canvas'].cancelFullScreen();
1143
IMG_Init: function(flags) {
1144
return flags; // We support JPG, PNG, TIF because browsers do
1147
IMG_Load__deps: ['SDL_LockSurface'],
1148
IMG_Load: function(filename) {
1149
filename = FS.standardizePath(Pointer_stringify(filename));
1150
if (filename[0] == '/') {
1151
// Convert the path to relative
1152
filename = filename.substr(1);
1154
var raw = Module["preloadedImages"][filename];
1156
if (raw === null) Module.printErr('Trying to reuse preloaded image, but freePreloadedMediaOnUse is set!');
1157
Runtime.warnOnce('Cannot find preloaded image ' + filename);
1160
if (Module['freePreloadedMediaOnUse']) {
1161
Module["preloadedImages"][filename] = null;
1163
var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
1164
var surfData = SDL.surfaces[surf];
1165
surfData.ctx.globalCompositeOperation = "copy";
1166
surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height);
1167
surfData.ctx.globalCompositeOperation = "source-over";
1168
// XXX SDL does not specify that loaded images must have available pixel data, in fact
1169
// there are cases where you just want to blit them, so you just need the hardware
1170
// accelerated version. However, code everywhere seems to assume that the pixels
1171
// are in fact available, so we retrieve it here. This does add overhead though.
1172
_SDL_LockSurface(surf);
1173
surfData.locked--; // The surface is not actually locked in this hack
1175
// After getting the pixel data, we can free the canvas and context if we do not need to do 2D canvas blitting
1176
surfData.canvas = surfData.ctx = null;
1180
SDL_LoadBMP: 'IMG_Load',
1181
SDL_LoadBMP_RW: 'IMG_Load',
1182
IMG_Load_RW: 'IMG_Load',
1186
// TODO fix SDL_OpenAudio, and add some tests for it. It's currently broken.
1187
SDL_OpenAudio: function(desired, obtained) {
1188
SDL.allocateChannels(32);
1190
// FIXME: Assumes 16-bit audio
1191
assert(obtained === 0, 'Cannot return obtained SDL audio params');
1194
freq: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.freq', 'i32', 0, 1) }}},
1195
format: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.format', 'i16', 0, 1) }}},
1196
channels: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.channels', 'i8', 0, 1) }}},
1197
samples: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.samples', 'i16', 0, 1) }}},
1198
callback: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.callback', 'void*', 0, 1) }}},
1199
userdata: {{{ makeGetValue('desired', 'SDL.structs.AudioSpec.userdata', 'void*', 0, 1) }}},
1204
var totalSamples = SDL.audio.samples*SDL.audio.channels;
1205
SDL.audio.bufferSize = totalSamples*2; // hardcoded 16-bit audio
1206
SDL.audio.buffer = _malloc(SDL.audio.bufferSize);
1207
SDL.audio.caller = function() {
1208
Runtime.dynCall('viii', SDL.audio.callback, [SDL.audio.userdata, SDL.audio.buffer, SDL.audio.bufferSize]);
1209
SDL.audio.pushAudio(SDL.audio.buffer, SDL.audio.bufferSize);
1211
// Mozilla Audio API. TODO: Other audio APIs
1213
SDL.audio.mozOutput = new Audio();
1214
SDL.audio.mozOutput['mozSetup'](SDL.audio.channels, SDL.audio.freq); // use string attributes on mozOutput for closure compiler
1215
SDL.audio.mozBuffer = new Float32Array(totalSamples);
1216
SDL.audio.pushAudio = function(ptr, size) {
1217
var mozBuffer = SDL.audio.mozBuffer;
1218
for (var i = 0; i < totalSamples; i++) {
1219
mozBuffer[i] = ({{{ makeGetValue('ptr', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
1221
SDL.audio.mozOutput['mozWriteAudio'](mozBuffer);
1226
if (!SDL.audio) return -1;
1230
SDL_PauseAudio: function(pauseOn) {
1231
if (SDL.audio.paused !== pauseOn) {
1232
SDL.audio.timer = pauseOn ? SDL.audio.timer && clearInterval(SDL.audio.timer) : setInterval(SDL.audio.caller, 1/35);
1234
SDL.audio.paused = pauseOn;
1237
SDL_CloseAudio__deps: ['SDL_PauseAudio', 'free'],
1238
SDL_CloseAudio: function() {
1241
_free(SDL.audio.buffer);
1246
SDL_LockAudio: function() {},
1247
SDL_UnlockAudio: function() {},
1249
SDL_CreateMutex: function() { return 0 },
1250
SDL_LockMutex: function() {},
1251
SDL_UnlockMutex: function() {},
1252
SDL_mutexP: function() { return 0 },
1253
SDL_mutexV: function() { return 0 },
1254
SDL_DestroyMutex: function() {},
1256
SDL_CreateCond: function() { return 0 },
1257
SDL_CondSignal: function() {},
1258
SDL_CondWait: function() {},
1259
SDL_DestroyCond: function() {},
1261
SDL_StartTextInput: function() {
1262
SDL.textInput = true;
1264
SDL_StopTextInput: function() {
1265
SDL.textInput = false;
1270
Mix_Init: function(flags) {
1271
if (!flags) return 0;
1272
return 8; /* MIX_INIT_OGG */
1274
Mix_Quit: function(){},
1276
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
1277
SDL.allocateChannels(32);
1278
// Just record the values for a later call to Mix_QuickLoad_RAW
1279
SDL.mixerFrequency = frequency;
1280
SDL.mixerFormat = format;
1281
SDL.mixerNumChannels = channels;
1282
SDL.mixerChunkSize = chunksize;
1286
Mix_CloseAudio: 'SDL_CloseAudio',
1288
Mix_AllocateChannels: function(num) {
1289
SDL.allocateChannels(num);
1293
Mix_ChannelFinished: function(func) {
1294
SDL.channelFinished = func;
1297
Mix_Volume: function(channel, volume) {
1298
if (channel == -1) {
1299
for (var i = 0; i < SDL.numChannels-1; i++) {
1300
_Mix_Volume(i, volume);
1302
return _Mix_Volume(SDL.numChannels-1, volume);
1304
return SDL.setGetVolume(SDL.channels[channel], volume);
1307
Mix_SetPanning: function() {
1311
Mix_LoadWAV_RW: function(filename, freesrc) {
1312
filename = FS.standardizePath(Pointer_stringify(filename));
1313
var raw = Module["preloadedAudios"][filename];
1315
if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
1316
Runtime.warnOnce('Cannot find preloaded audio ' + filename);
1319
if (Module['freePreloadedMediaOnUse']) {
1320
Module["preloadedAudios"][filename] = null;
1322
var id = SDL.audios.length;
1323
// Keep the loaded audio in the audio arrays, ready for playback
1331
Mix_QuickLoad_RAW: function(mem, len) {
1332
var audio = new Audio();
1333
// Record the number of channels and frequency for later usage
1334
audio.numChannels = SDL.mixerNumChannels;
1335
audio.frequency = SDL.mixerFrequency;
1336
var numSamples = len >> 1; // len is the length in bytes, and the array contains 16-bit PCM values
1337
var buffer = new Float32Array(numSamples);
1338
for (var i = 0; i < numSamples; ++i) {
1339
buffer[i] = ({{{ makeGetValue('mem', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
1341
// FIXME: doesn't make sense to keep the audio element in the buffer
1342
var id = SDL.audios.length;
1351
Mix_FreeChunk: function(id) {
1352
SDL.audios[id] = null;
1354
Mix_ReserveChannels: function(num) {
1355
SDL.channelMinimumNumber = num;
1357
Mix_PlayChannel: function(channel, id, loops) {
1358
// TODO: handle loops
1360
// Get the audio element associated with the ID
1361
var info = SDL.audios[id];
1362
if (!info) return -1;
1363
var audio = info.audio;
1364
if (!audio) return -1;
1366
// If the user asks us to allocate a channel automatically, get the first
1368
if (channel == -1) {
1369
channel = SDL.channelMinimumNumber;
1370
for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) {
1371
if (!SDL.channels[i].audio) {
1378
// We clone the audio node to utilize the preloaded audio buffer, since
1379
// the browser has already preloaded the audio file.
1380
var channelInfo = SDL.channels[channel];
1381
channelInfo.audio = audio = audio.cloneNode(true);
1382
audio.numChannels = info.audio.numChannels;
1383
audio.frequency = info.audio.frequency;
1384
if (SDL.channelFinished) {
1385
audio['onended'] = function() { // TODO: cache these
1386
Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
1389
// Either play the element, or load the dynamic data into it
1391
var contextCtor = null;
1392
if (audio && ('mozSetup' in audio)) { // Audio Data API
1394
audio['mozSetup'](audio.numChannels, audio.frequency);
1395
audio["mozWriteAudio"](info.buffer);
1397
// Workaround for Firefox bug 783052
1398
// ignore this exception!
1401
} else if (contextCtor = (window.AudioContext || // WebAudio API
1402
window.webkitAudioContext)) {
1403
var currentIndex = 0;
1404
var numChannels = parseInt(audio.numChannels);
1405
var context = new contextCtor();
1406
var source = context.createBufferSource();
1407
source.loop = false;
1408
source.buffer = context.createBuffer(numChannels, 1, audio.frequency);
1409
var jsNode = context.createJavaScriptNode(2048, numChannels, numChannels);
1410
jsNode.onaudioprocess = function(event) {
1411
var buffers = new Array(numChannels);
1412
for (var i = 0; i < numChannels; ++i) {
1413
buffers[i] = event.outputBuffer.getChannelData(i);
1415
var remaining = info.buffer.length - currentIndex;
1416
if (remaining > 2048) {
1419
for (var i = 0; i < remaining;) {
1420
for (var j = 0; j < numChannels; ++j) {
1421
buffers[j][i] = info.buffer[currentIndex + i + j] * audio.volume;
1425
currentIndex += remaining * numChannels;
1426
for (var i = remaining; i < 2048;) {
1427
for (var j = 0; j < numChannels; ++j) {
1428
buffers[j][i] = 0; // silence
1433
source.connect(jsNode);
1434
jsNode.connect(context.destination);
1441
audio.volume = channelInfo.volume;
1442
audio.paused = false;
1445
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
1447
Mix_FadingChannel: function(channel) {
1448
return 0; // MIX_NO_FADING, TODO
1451
Mix_HaltChannel: function(channel) {
1452
var info = SDL.channels[channel];
1457
if (SDL.channelFinished) {
1458
Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
1463
Mix_HookMusicFinished__deps: ['Mix_HaltMusic'],
1464
Mix_HookMusicFinished: function(func) {
1465
SDL.hookMusicFinished = func;
1466
if (SDL.music.audio) { // ensure the callback will be called, if a music is already playing
1467
SDL.music.audio['onended'] = _Mix_HaltMusic;
1471
Mix_VolumeMusic: function(volume) {
1472
return SDL.setGetVolume(SDL.music, volume);
1475
Mix_LoadMUS: 'Mix_LoadWAV_RW',
1476
Mix_LoadMUS_RW: 'Mix_LoadWAV_RW',
1478
Mix_FreeMusic: 'Mix_FreeChunk',
1480
Mix_PlayMusic__deps: ['Mix_HaltMusic'],
1481
Mix_PlayMusic: function(id, loops) {
1482
loops = Math.max(loops, 1);
1483
var audio = SDL.audios[id].audio;
1484
if (!audio) return 0;
1485
audio.loop = loops != 1; // TODO: handle N loops for finite N
1486
if (SDL.audios[id].buffer) {
1487
audio["mozWriteAudio"](SDL.audios[id].buffer);
1491
audio.volume = SDL.music.volume;
1492
audio['onended'] = _Mix_HaltMusic; // will send callback
1493
SDL.music.audio = audio;
1497
Mix_PauseMusic: function() {
1498
var audio = SDL.music.audio;
1499
if (!audio) return 0;
1504
Mix_ResumeMusic: function() {
1505
var audio = SDL.music.audio;
1506
if (!audio) return 0;
1511
Mix_HaltMusic: function() {
1512
var audio = SDL.music.audio;
1513
if (!audio) return 0;
1514
audio.src = audio.src; // rewind
1516
SDL.music.audio = null;
1517
if (SDL.hookMusicFinished) {
1518
Runtime.dynCall('v', SDL.hookMusicFinished);
1523
Mix_FadeInMusicPos: 'Mix_PlayMusic', // XXX ignore fading in effect
1525
Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect
1527
Mix_PlayingMusic: function() {
1528
return (SDL.music.audio && !SDL.music.audio.paused) ? 1 : 0;
1531
// http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_38.html#SEC38
1532
// "Note: Does not check if the channel has been paused."
1533
Mix_Playing: function(channel) {
1534
if (channel === -1) {
1536
for (var i = 0; i < SDL.channels.length; i++) {
1537
count += _Mix_Playing(i);
1541
var info = SDL.channels[channel];
1542
if (info && info.audio && !info.audio.paused) {
1548
Mix_Pause: function(channel) {
1549
if (channel === -1) {
1550
for (var i = 0; i<SDL.channels.length;i++) {
1555
var info = SDL.channels[channel];
1556
if (info && info.audio) {
1558
info.audio.paused = true;
1562
// http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_39.html#SEC39
1563
Mix_Paused: function(channel) {
1564
if (channel === -1) {
1565
var pausedCount = 0;
1566
for (var i = 0; i<SDL.channels.length;i++) {
1567
pausedCount += _Mix_Paused(i);
1571
var info = SDL.channels[channel];
1572
if (info && info.audio && info.audio.paused) {
1578
Mix_PausedMusic: function() {
1579
return (SDL.music.audio && SDL.music.audio.paused) ? 1 : 0;
1582
// http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_33.html#SEC33
1583
Mix_Resume: function(channel) {
1584
if (channel === -1) {
1585
for (var i = 0; i<SDL.channels.length;i++) {
1590
var info = SDL.channels[channel];
1591
if (info && info.audio) {
1598
TTF_Init: function() { return 0 },
1600
TTF_OpenFont: function(filename, size) {
1601
filename = FS.standardizePath(Pointer_stringify(filename));
1602
var id = SDL.fonts.length;
1604
name: filename, // but we don't actually do anything with it..
1610
TTF_CloseFont: function(font) {
1611
SDL.fonts[font] = null;
1614
TTF_RenderText_Solid: function(font, text, color) {
1615
// XXX the font and color are ignored
1616
text = Pointer_stringify(text) || ' '; // if given an empty string, still return a valid surface
1617
var fontData = SDL.fonts[font];
1618
var w = SDL.estimateTextWidth(fontData, text);
1619
var h = fontData.size;
1620
var color = SDL.loadColorToCSSRGB(color); // XXX alpha breaks fonts?
1621
var fontString = h + 'px sans-serif';
1622
var surf = SDL.makeSurface(w, h, 0, false, 'text:' + text); // bogus numbers..
1623
var surfData = SDL.surfaces[surf];
1624
surfData.ctx.save();
1625
surfData.ctx.fillStyle = color;
1626
surfData.ctx.font = fontString;
1627
surfData.ctx.textBaseline = 'top';
1628
surfData.ctx.fillText(text, 0, 0);
1629
surfData.ctx.restore();
1632
TTF_RenderText_Blended: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
1633
TTF_RenderText_Shaded: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
1635
TTF_SizeText: function(font, text, w, h) {
1636
var fontData = SDL.fonts[font];
1638
{{{ makeSetValue('w', '0', 'SDL.estimateTextWidth(fontData, Pointer_stringify(text))', 'i32') }}};
1641
{{{ makeSetValue('h', '0', 'fontData.size', 'i32') }}};
1646
TTF_FontAscent: function(font) {
1647
var fontData = SDL.fonts[font];
1648
return Math.floor(fontData.size*0.98); // XXX
1651
TTF_FontDescent: function(font) {
1652
var fontData = SDL.fonts[font];
1653
return Math.floor(fontData.size*0.02); // XXX
1658
boxRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
1659
var surfData = SDL.surfaces[surf];
1660
assert(!surfData.locked); // but we could unlock and re-lock if we must..
1661
// TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
1662
surfData.ctx.save();
1663
surfData.ctx.fillStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
1664
surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
1665
surfData.ctx.restore();
1668
rectangleRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
1669
var surfData = SDL.surfaces[surf];
1670
assert(!surfData.locked); // but we could unlock and re-lock if we must..
1671
surfData.ctx.save();
1672
surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
1673
surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
1674
surfData.ctx.restore();
1677
lineRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
1678
var surfData = SDL.surfaces[surf];
1679
assert(!surfData.locked); // but we could unlock and re-lock if we must..
1680
surfData.ctx.save();
1681
surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
1682
surfData.ctx.beginPath();
1683
surfData.ctx.moveTo(x1, y1);
1684
surfData.ctx.lineTo(x2, y2);
1685
surfData.ctx.stroke();
1686
surfData.ctx.restore();
1689
pixelRGBA__deps: ['boxRGBA'],
1690
pixelRGBA: function(surf, x1, y1, r, g, b, a) {
1691
// This cannot be fast, to render many pixels this way!
1692
_boxRGBA(surf, x1, y1, x1, y1, r, g, b, a);
1697
SDL_GL_SetAttribute: function(attr, value) {
1698
console.log('TODO: SDL_GL_SetAttribute');
1701
SDL_GL_GetProcAddress__deps: ['$GLEmulation'],
1702
SDL_GL_GetProcAddress: function(name_) {
1703
return GLEmulation.getProcAddress(Pointer_stringify(name_));
1706
SDL_GL_SwapBuffers: function() {},
1710
SDL_SetGamma: function(r, g, b) {
1716
SDL_InitSubSystem: function(flags) { return 0 },
1718
SDL_NumJoysticks: function() { return 0 },
1720
SDL_RWFromFile: function(filename, mode) {
1721
return filename; // XXX We just forward the filename
1724
SDL_EnableUNICODE: function(on) {
1725
var ret = SDL.unicode || 0;
1730
SDL_AddTimer: function(interval, callback, param) {
1731
return window.setTimeout(function() {
1732
Runtime.dynCall('ii', callback, [interval, param]);
1735
SDL_RemoveTimer: function(id) {
1736
window.clearTimeout(id);
1740
SDL_CreateThread: function() {
1741
throw 'SDL threads cannot be supported in the web platform because they assume shared state. See emscripten_create_worker etc. for a message-passing concurrency model that does let you run code in another thread.'
1744
SDL_WaitThread: function() { throw 'SDL_WaitThread' },
1745
SDL_GetThreadID: function() { throw 'SDL_GetThreadID' },
1746
SDL_ThreadID: function() { throw 'SDL_ThreadID' },
1747
SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' },
1748
SDL_FreeRW: function() { throw 'SDL_FreeRW: TODO' },
1749
SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' },
1750
SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' },
1751
SDL_WM_ToggleFullScreen: function() { throw 'SDL_WM_ToggleFullScreen: TODO' },
1753
Mix_SetPostMix: function() { throw 'Mix_SetPostMix: TODO' },
1754
Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' },
1755
Mix_FadeInChannelTimed: function() { throw 'Mix_FadeInChannelTimed' },
1756
Mix_FadeOutChannel: function() { throw 'Mix_FadeOutChannel' },
1758
Mix_Linked_Version: function() { throw 'Mix_Linked_Version: TODO' },
1759
SDL_CreateRGBSurfaceFrom: function() { throw 'SDL_CreateRGBSurfaceFrom: TODO' },
1760
SDL_SaveBMP_RW: function() { throw 'SDL_SaveBMP_RW: TODO' },
1763
autoAddDeps(LibrarySDL, '$SDL');
1764
mergeInto(LibraryManager.library, LibrarySDL);