~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to src/library_sdl.js

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//"use strict";
 
2
 
 
3
// See browser tests for examples (tests/runner.py, search for sdl_). Run with
 
4
//    python tests/runner.py browser
 
5
 
 
6
// Notes:
 
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
 
10
//                   or otherwise).
 
11
 
 
12
var LibrarySDL = {
 
13
  $SDL__deps: ['$FS', '$Browser'],
 
14
  $SDL: {
 
15
    defaults: {
 
16
      width: 320,
 
17
      height: 200,
 
18
      copyOnLock: true
 
19
    },
 
20
 
 
21
    version: null,
 
22
 
 
23
    surfaces: {},
 
24
    events: [],
 
25
    fonts: [null],
 
26
 
 
27
    // The currently preloaded audio elements ready to be played
 
28
    audios: [null],
 
29
    // The currently playing audio element.  There's only one music track.
 
30
    music: {
 
31
      audio: null,
 
32
      volume: 1.0
 
33
    },
 
34
    mixerFrequency: 22050,
 
35
    mixerFormat: 0x8010, // AUDIO_S16LSB
 
36
    mixerNumChannels: 2,
 
37
    mixerChunkSize: 1024,
 
38
    channelMinimumNumber: 0,
 
39
 
 
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
 
42
 
 
43
    keyboardState: null,
 
44
    keyboardMap: {},
 
45
 
 
46
    textInput: false,
 
47
 
 
48
    startTime: null,
 
49
    mouseX: 0,
 
50
    mouseY: 0,
 
51
    buttonState: 0,
 
52
    DOMButtons: [0, 0, 0],
 
53
 
 
54
    DOMEventToSDLEvent: {},
 
55
 
 
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'
 
58
      38:  1106, // up arrow
 
59
      40:  1105, // down arrow
 
60
      37:  1104, // left arrow
 
61
      39:  1103, // right arrow
 
62
 
 
63
      33: 1099, // pagedup
 
64
      34: 1102, // pagedown
 
65
 
 
66
      17:  1248, // control (right, or left)
 
67
      18:  1250, // alt
 
68
      173: 45, // minus
 
69
      16:  1249, // shift
 
70
      
 
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
 
81
 
 
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
 
94
 
 
95
      188: 44, // comma
 
96
      190: 46, // period
 
97
      191: 47, // slash (/)
 
98
      192: 96, // backtick/backquote (`)
 
99
    },
 
100
 
 
101
    scanCodes: { // SDL keycode ==> SDL scancode. See SDL_scancode.h
 
102
      97: 4, // A
 
103
      98: 5,
 
104
      99: 6,
 
105
      100: 7,
 
106
      101: 8,
 
107
      102: 9,
 
108
      103: 10,
 
109
      104: 11,
 
110
      105: 12,
 
111
      106: 13,
 
112
      107: 14,
 
113
      108: 15,
 
114
      109: 16,
 
115
      110: 17,
 
116
      111: 18,
 
117
      112: 19,
 
118
      113: 20,
 
119
      114: 21,
 
120
      115: 22,
 
121
      116: 23,
 
122
      117: 24,
 
123
      118: 25,
 
124
      119: 26,
 
125
      120: 27,
 
126
      121: 28,
 
127
      122: 29, // Z
 
128
      44: 54, // comma
 
129
      46: 55, // period
 
130
      47: 56, // slash
 
131
      49: 30, // 1
 
132
      50: 31,
 
133
      51: 32,
 
134
      52: 33,
 
135
      53: 34,
 
136
      54: 35,
 
137
      55: 36,
 
138
      56: 37,
 
139
      57: 38, // 9
 
140
      48: 39, // 0
 
141
      13: 40, // return
 
142
      9: 43, // tab
 
143
      27: 41, // escape
 
144
      32: 44, // space
 
145
      92: 49, // backslash
 
146
      305: 224, // ctrl
 
147
      308: 226, // alt
 
148
    },
 
149
 
 
150
    structs: {
 
151
      Rect: Runtime.generateStructInfo([
 
152
        ['i32', 'x'], ['i32', 'y'], ['i32', 'w'], ['i32', 'h'], 
 
153
      ]),
 
154
      PixelFormat: Runtime.generateStructInfo([
 
155
        ['i32', 'format'],
 
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']
 
161
      ]),
 
162
      KeyboardEvent: Runtime.generateStructInfo([
 
163
        ['i32', 'type'],
 
164
        ['i32', 'windowID'],
 
165
        ['i8', 'state'],
 
166
        ['i8', 'repeat'],
 
167
        ['i8', 'padding2'],
 
168
        ['i8', 'padding3'],
 
169
        ['i32', 'keysym']
 
170
      ]),
 
171
      keysym: Runtime.generateStructInfo([
 
172
        ['i32', 'scancode'],
 
173
        ['i32', 'sym'],
 
174
        ['i16', 'mod'],
 
175
        ['i32', 'unicode']
 
176
      ]),
 
177
      TextInputEvent: Runtime.generateStructInfo([
 
178
        ['i32', 'type'],
 
179
        ['i32', 'windowID'],
 
180
        ['b256', 'text'],
 
181
      ]),
 
182
      MouseMotionEvent: Runtime.generateStructInfo([
 
183
        ['i32', 'type'],
 
184
        ['i32', 'windowID'],
 
185
        ['i8', 'state'],
 
186
        ['i8', 'padding1'],
 
187
        ['i8', 'padding2'],
 
188
        ['i8', 'padding3'],
 
189
        ['i32', 'x'],
 
190
        ['i32', 'y'],
 
191
        ['i32', 'xrel'],
 
192
        ['i32', 'yrel']
 
193
      ]),
 
194
      MouseButtonEvent: Runtime.generateStructInfo([
 
195
        ['i32', 'type'],
 
196
        ['i32', 'windowID'],
 
197
        ['i8', 'button'],
 
198
        ['i8', 'state'],
 
199
        ['i8', 'padding1'],
 
200
        ['i8', 'padding2'],
 
201
        ['i32', 'x'],
 
202
        ['i32', 'y']
 
203
      ]),
 
204
      ResizeEvent: Runtime.generateStructInfo([
 
205
        ['i32', 'type'],
 
206
        ['i32', 'w'],
 
207
        ['i32', 'h']
 
208
      ]),
 
209
      AudioSpec: Runtime.generateStructInfo([
 
210
        ['i32', 'freq'],
 
211
        ['i16', 'format'],
 
212
        ['i8', 'channels'],
 
213
        ['i8', 'silence'],
 
214
        ['i16', 'samples'],
 
215
        ['i32', 'size'],
 
216
        ['void*', 'callback'],
 
217
        ['void*', 'userdata']
 
218
      ]),
 
219
      version: Runtime.generateStructInfo([
 
220
        ['i8', 'major'],
 
221
        ['i8', 'minor'],
 
222
        ['i8', 'patch']
 
223
      ])
 
224
    },
 
225
 
 
226
    loadRect: function(rect) {
 
227
      return {
 
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') }}}
 
232
      };
 
233
    },
 
234
 
 
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) + ')';
 
239
    },
 
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) + ')';
 
243
    },
 
244
 
 
245
    translateColorToCSSRGBA: function(rgba) {
 
246
      return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
 
247
    },
 
248
 
 
249
    translateRGBAToCSSRGBA: function(r, g, b, a) {
 
250
      return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
 
251
    },
 
252
 
 
253
    translateRGBAToColor: function(r, g, b, a) {
 
254
      return (r << 24) + (g << 16) + (b << 8) + a;
 
255
    },
 
256
 
 
257
    makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) {
 
258
      flags = flags || 0;
 
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
 
263
 
 
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;
 
267
 
 
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
 
276
 
 
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') }}}
 
281
 
 
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') }}}
 
286
 
 
287
      // Decide if we want to use WebGL or not
 
288
      var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL
 
289
      SDL.GL = SDL.GL || useWebGL;
 
290
      var canvas;
 
291
      if (!usePageCanvas) {
 
292
        canvas = document.createElement('canvas');
 
293
        canvas.width = width;
 
294
        canvas.height = height;
 
295
      } else {
 
296
        canvas = Module['canvas'];
 
297
      }
 
298
      var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas);
 
299
      SDL.surfaces[surf] = {
 
300
        width: width,
 
301
        height: height,
 
302
        canvas: canvas,
 
303
        ctx: ctx,
 
304
        surf: surf,
 
305
        buffer: buffer,
 
306
        pixelFormat: pixelFormat,
 
307
        alpha: 255,
 
308
        flags: flags,
 
309
        locked: 0,
 
310
        usePageCanvas: usePageCanvas,
 
311
        source: source,
 
312
 
 
313
        isFlagSet: function (flag) {
 
314
          return flags & flag;
 
315
        }
 
316
      };
 
317
 
 
318
      return surf;
 
319
    },
 
320
 
 
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) {
 
327
        return;
 
328
      }
 
329
      
 
330
      var fullWidth  = Module['canvas'].width;
 
331
      var fullHeight = Module['canvas'].height;
 
332
 
 
333
      var startX  = rX || 0;
 
334
      var startY  = rY || 0;
 
335
      var endX    = (rW || (fullWidth - startX)) + startX;
 
336
      var endY    = (rH || (fullHeight - startY)) + startY;
 
337
      
 
338
      var buffer  = surfData.buffer;
 
339
      var data    = surfData.image.data;
 
340
      var colors  = surfData.colors;
 
341
 
 
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;
 
349
 
 
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];
 
354
        }
 
355
      }
 
356
    },
 
357
 
 
358
    freeSurface: function(surf) {
 
359
      _free(SDL.surfaces[surf].buffer);
 
360
      _free(SDL.surfaces[surf].pixelFormat);
 
361
      _free(surf);
 
362
      SDL.surfaces[surf] = null;
 
363
    },
 
364
 
 
365
    receiveEvent: function(event) {
 
366
      switch(event.type) {
 
367
        case 'mousemove':
 
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'];
 
373
            }
 
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)
 
378
              return false;
 
379
            }
 
380
          }
 
381
          // fall through
 
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;
 
385
            var event2 = {
 
386
              type: 'mousedown',
 
387
              button: button,
 
388
              pageX: event.pageX,
 
389
              pageY: event.pageY
 
390
            };
 
391
            SDL.events.push(event2);
 
392
            event = {
 
393
              type: 'mouseup',
 
394
              button: button,
 
395
              pageX: event.pageX,
 
396
              pageY: event.pageY
 
397
            };
 
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;
 
404
          }
 
405
 
 
406
          if (event.type == 'keypress' && !SDL.textInput) {
 
407
            break;
 
408
          }
 
409
 
 
410
          SDL.events.push(event);
 
411
          break;
 
412
        case 'mouseout':
 
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]) {
 
416
              SDL.events.push({
 
417
                type: 'mouseup',
 
418
                button: i,
 
419
                pageX: event.pageX,
 
420
                pageY: event.pageY
 
421
              });
 
422
              SDL.DOMButtons[i] = 0;
 
423
            }
 
424
          }
 
425
          break;
 
426
        case 'blur':
 
427
        case 'visibilitychange': {
 
428
          // Un-press all pressed keys: TODO
 
429
          for (var code in SDL.keyboardMap) {
 
430
            SDL.events.push({
 
431
              type: 'keyup',
 
432
              keyCode: SDL.keyboardMap[code]
 
433
            });
 
434
          }
 
435
          break;
 
436
        }
 
437
        case 'unload':
 
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();
 
442
          }
 
443
          return true;
 
444
        case 'resize':
 
445
          SDL.events.push(event);
 
446
          break;
 
447
      }
 
448
      if (SDL.events.length >= 10000) {
 
449
        Module.printErr('SDL event queue full, dropping events');
 
450
        SDL.events = SDL.events.slice(0, 10000);
 
451
      }
 
452
      return false;
 
453
    },
 
454
 
 
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
 
459
        return;
 
460
      }
 
461
 
 
462
      switch(event.type) {
 
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
 
469
          } else {
 
470
            key = SDL.keyCodes[event.keyCode] || event.keyCode;
 
471
          }
 
472
          var scan;
 
473
          if (key >= 1024) {
 
474
            scan = key - 1024;
 
475
          } else {
 
476
            scan = SDL.scanCodes[key] || key;
 
477
          }
 
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
 
482
 
 
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') }}}
 
487
 
 
488
          var code = SDL.keyCodes[event.keyCode] || event.keyCode;
 
489
          {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}};
 
490
          if (down) {
 
491
            SDL.keyboardMap[code] = event.keyCode; // save the DOM input, which we can use to unpress it during blur
 
492
          } else {
 
493
            delete SDL.keyboardMap[code];
 
494
          }
 
495
 
 
496
          break;
 
497
        }
 
498
        case 'keypress': {
 
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') }}};
 
504
          }
 
505
          break;
 
506
        }
 
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
 
511
            // correct.
 
512
            SDL.buttonState |= 1 << event.button;
 
513
          } else if (event.type == 'mouseup') {
 
514
            SDL.buttonState &= ~(1 << event.button);
 
515
          }
 
516
          // fall through
 
517
        case 'mousemove': {
 
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;
 
525
            } else {
 
526
              var movementX = Browser.getMovementX(event);
 
527
              var movementY = Browser.getMovementY(event);
 
528
            }
 
529
            var x = SDL.mouseX + movementX;
 
530
            var y = SDL.mouseY + movementY;
 
531
          } else {
 
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);
 
537
 
 
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);
 
545
 
 
546
            var movementX = x - SDL.mouseX;
 
547
            var movementY = y - SDL.mouseY;
 
548
          }
 
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') }}};
 
556
          } else {
 
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') }}};
 
563
          }
 
564
          SDL.mouseX = x;
 
565
          SDL.mouseY = y;
 
566
          break;
 
567
        }
 
568
        case 'unload': {
 
569
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
 
570
          break;
 
571
        }
 
572
        case 'resize': {
 
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') }}};
 
576
          break;
 
577
        }
 
578
        default: throw 'Unhandled SDL event: ' + event.type;
 
579
      }
 
580
    },
 
581
 
 
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;
 
587
      tempCtx.save();
 
588
      tempCtx.font = fontString;
 
589
      var ret = tempCtx.measureText(text).width | 0;
 
590
      tempCtx.restore();
 
591
      return ret;
 
592
    },
 
593
 
 
594
    // Sound
 
595
 
 
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;
 
603
      SDL.channels = [];
 
604
      for (var i = 0; i < num; i++) {
 
605
        SDL.channels[i] = {
 
606
          audio: null,
 
607
          volume: 1.0
 
608
        };
 
609
      }
 
610
    },
 
611
 
 
612
    setGetVolume: function(info, volume) {
 
613
      if (!info) return 0;
 
614
      var ret = info.volume * 128; // MIX_MAX_VOLUME
 
615
      if (volume != -1) {
 
616
        info.volume = volume / 128;
 
617
        if (info.audio) info.audio.volume = info.volume;
 
618
      }
 
619
      return ret;
 
620
    },
 
621
 
 
622
    // Debugging
 
623
 
 
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]]);
 
631
      }
 
632
    }
 
633
  },
 
634
 
 
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') }}}
 
641
    }
 
642
    return SDL.version;
 
643
  },
 
644
 
 
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);
 
654
    }
 
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 */;
 
667
    return 0; // success
 
668
  },
 
669
 
 
670
  SDL_WasInit__deps: ['SDL_Init'],
 
671
  SDL_WasInit: function() {
 
672
    if (SDL.startTime === null) {
 
673
      _SDL_Init();
 
674
    }
 
675
    return 1;
 
676
  },
 
677
 
 
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') }}}
 
686
    return ret;
 
687
  },
 
688
 
 
689
  SDL_ListModes: function(format, flags) {
 
690
    return -1; // -1 == all modes are ok. TODO
 
691
  },
 
692
 
 
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.
 
697
  },
 
698
 
 
699
  SDL_VideoDriverName: function(buf, max_size) {
 
700
    if (SDL.startTime === null) {
 
701
      return 0; //return NULL
 
702
    }
 
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];
 
706
 
 
707
    var index = 0;
 
708
    var size  = driverName.length;
 
709
 
 
710
    if (max_size <= size) {
 
711
      size = max_size - 1; //-1 cause null-terminator
 
712
    }
 
713
 
 
714
    while (index < size) {
 
715
        var value = driverName[index];
 
716
        {{{ makeSetValue('buf', 'index', 'value', 'i8') }}};
 
717
        index++;
 
718
    }
 
719
 
 
720
    {{{ makeSetValue('buf', 'index', '0', 'i8') }}};
 
721
    return buf;
 
722
  },
 
723
 
 
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);
 
727
    });
 
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) {
 
733
        SDL.receiveEvent({
 
734
          type: 'resize',
 
735
          w: w,
 
736
          h: h
 
737
        });
 
738
      });
 
739
    }
 
740
    return SDL.screen;
 
741
  },
 
742
 
 
743
  SDL_GetVideoSurface: function() {
 
744
    return SDL.screen;
 
745
  },
 
746
 
 
747
  SDL_QuitSubSystem: function(flags) {
 
748
    Module.print('SDL_QuitSubSystem called (and ignored)');
 
749
  },
 
750
 
 
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();
 
755
      }
 
756
    }
 
757
    if (SDL.music.audio) {
 
758
      SDL.music.audio.pause();
 
759
    }
 
760
    Module.print('SDL_Quit called (and ignored)');
 
761
  },
 
762
 
 
763
  // Copy data from the canvas backing to a C++-accessible storage
 
764
  SDL_LockSurface: function(surf) {
 
765
    var surfData = SDL.surfaces[surf];
 
766
 
 
767
    surfData.locked++;
 
768
    if (surfData.locked > 1) return 0;
 
769
 
 
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
 
776
      }
 
777
    }
 
778
 
 
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:
 
787
        //
 
788
        // var size = surfData.width * surfData.height;
 
789
        // var data = '';
 
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], 
 
795
        //     255);
 
796
        //   var index = surfData.colorMap[color];
 
797
        //   {{{ makeSetValue('surfData.buffer', 'i', 'index', 'i8') }}};
 
798
        // }
 
799
        throw 'CopyOnLock is not supported for SDL_LockSurface with SDL_HWPALETTE flag set' + new Error().stack;
 
800
      } else {
 
801
#if USE_TYPED_ARRAYS == 2
 
802
      HEAPU8.set(surfData.image.data, surfData.buffer);
 
803
#else
 
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') }}};
 
807
      }
 
808
#endif
 
809
      }
 
810
    }
 
811
 
 
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*') }}};
 
817
 
 
818
    return 0;
 
819
  },
 
820
 
 
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
 
824
 
 
825
    var surfData = SDL.surfaces[surf];
 
826
 
 
827
    surfData.locked--;
 
828
    if (surfData.locked > 0) return;
 
829
 
 
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;
 
840
      var dst = 0;
 
841
      var isScreen = surf == SDL.screen;
 
842
      while (dst < num) {
 
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);
 
849
        src++;
 
850
        dst += 4;
 
851
      }
 
852
#else
 
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;
 
858
      }
 
859
#endif
 
860
    } else {
 
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];
 
875
        }
 
876
        s += width*3;
 
877
      }
 
878
    }
 
879
    // Copy to canvas
 
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
 
882
  },
 
883
 
 
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
 
887
  },
 
888
 
 
889
  SDL_UpdateRect: function(surf, x, y, w, h) {
 
890
    // We actually do the whole screen in Unlock...
 
891
  },
 
892
 
 
893
  SDL_UpdateRects: function(surf, numrects, rects) {
 
894
    // We actually do the whole screen in Unlock...
 
895
  },
 
896
 
 
897
  SDL_Delay: function(delay) {
 
898
    throw 'SDL_Delay called! Potential infinite loop, quitting. ' + new Error().stack;
 
899
  },
 
900
 
 
901
  SDL_WM_SetCaption: function(title, icon) {
 
902
    title = title && Pointer_stringify(title);
 
903
    icon = icon && Pointer_stringify(icon);
 
904
  },
 
905
 
 
906
  SDL_EnableKeyRepeat: function(delay, interval) {
 
907
    // TODO
 
908
  },
 
909
 
 
910
  SDL_GetKeyboardState: function() {
 
911
    return SDL.keyboardState;
 
912
  },
 
913
 
 
914
  SDL_GetKeyState__deps: ['SDL_GetKeyboardState'],
 
915
  SDL_GetKeyState: function() {
 
916
    return _SDL_GetKeyboardState();
 
917
  },
 
918
 
 
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
 
924
  },
 
925
 
 
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;
 
930
  },
 
931
 
 
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();
 
935
    SDL.events.push({
 
936
      type: 'mousemove',
 
937
      pageX: x + (window.scrollX + rect.left),
 
938
      pageY: y + (window.scrollY + rect.top)
 
939
    });
 
940
  },
 
941
 
 
942
  SDL_ShowCursor: function(toggle) {
 
943
    switch (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();
 
947
          return 0;
 
948
        } else { // else return SDL_ENABLE to indicate the failure
 
949
          return 1;
 
950
        }
 
951
        break;
 
952
      case 1: // SDL_ENABLE
 
953
        Module['canvas'].exitPointerLock();
 
954
        return 1;
 
955
        break;
 
956
      case -1: // SDL_QUERY
 
957
        return !Browser.pointerLock;
 
958
        break;
 
959
      default:
 
960
        console.log( "SDL_ShowCursor called with unknown toggle parameter value: " + toggle + "." );
 
961
        break;
 
962
    }
 
963
  },
 
964
 
 
965
  SDL_GetError: function() {
 
966
    return allocate(intArrayFromString("unknown SDL-emscripten error"), 'i8');
 
967
  },
 
968
 
 
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);
 
971
  },
 
972
 
 
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);
 
979
    return ret;
 
980
  },
 
981
 
 
982
  SDL_FreeSurface: function(surf) {
 
983
    if (surf) SDL.freeSurface(surf);
 
984
  },
 
985
 
 
986
  SDL_UpperBlit: function(src, srcrect, dst, dstrect) {
 
987
    var srcData = SDL.surfaces[src];
 
988
    var dstData = SDL.surfaces[dst];
 
989
    var sr, dr;
 
990
    if (srcrect) {
 
991
      sr = SDL.loadRect(srcrect);
 
992
    } else {
 
993
      sr = { x: 0, y: 0, w: srcData.width, h: srcData.height };
 
994
    }
 
995
    if (dstrect) {
 
996
      dr = SDL.loadRect(dstrect);
 
997
    } else {
 
998
      dr = { x: 0, y: 0, w: -1, h: -1 };
 
999
    }
 
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
 
1006
    }
 
1007
    return 0;
 
1008
  },
 
1009
 
 
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..
 
1013
    
 
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
 
1017
      //32 bit canvas
 
1018
      var index = color * 3;
 
1019
      color = SDL.translateRGBAToColor(surfData.colors[index], surfData.colors[index +1], surfData.colors[index +2], 255);
 
1020
    }
 
1021
 
 
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();
 
1027
  },
 
1028
 
 
1029
  SDL_BlitSurface__deps: ['SDL_UpperBlit'],
 
1030
  SDL_BlitSurface: function(src, srcrect, dst, dstrect) {
 
1031
    return _SDL_UpperBlit(src, srcrect, dst, dstrect);
 
1032
  },
 
1033
 
 
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);
 
1041
    return ret;
 
1042
  },
 
1043
 
 
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);
 
1054
    return ret;
 
1055
  },
 
1056
 
 
1057
  SDL_SetAlpha: function(surf, flag, alpha) {
 
1058
    SDL.surfaces[surf].alpha = alpha;
 
1059
  },
 
1060
 
 
1061
  SDL_GetTicks: function() {
 
1062
    return Math.floor(Date.now() - SDL.startTime);
 
1063
  },
 
1064
 
 
1065
  SDL_PollEvent: function(ptr) {
 
1066
    if (SDL.events.length === 0) return 0;
 
1067
    if (ptr) {
 
1068
      SDL.makeCEvent(SDL.events.shift(), ptr);
 
1069
    }
 
1070
    return 1;
 
1071
  },
 
1072
 
 
1073
  SDL_PushEvent: function(ptr) {
 
1074
    SDL.events.push(ptr); // XXX Should we copy it? Not clear from API
 
1075
    return 0;
 
1076
  },
 
1077
 
 
1078
  SDL_PeepEvents: function(events, numEvents, action, from, to) {
 
1079
    switch(action) {
 
1080
      case 2: { // SDL_GETEVENT
 
1081
        assert(numEvents == 1);
 
1082
        var got = 0;
 
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);
 
1087
          got++;
 
1088
          numEvents--;
 
1089
          // events += sizeof(..)
 
1090
        }
 
1091
        return got;
 
1092
      }
 
1093
      default: throw 'SDL_PeepEvents does not yet support that action: ' + action;
 
1094
    }
 
1095
  },
 
1096
 
 
1097
  SDL_PumpEvents: function(){},
 
1098
 
 
1099
  SDL_SetColors: function(surf, colors, firstColor, nColors) {
 
1100
    var surfData = SDL.surfaces[surf];
 
1101
 
 
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
 
1108
    } 
 
1109
 
 
1110
    for (var i = firstColor; i < firstColor + nColors; i++) {
 
1111
      var index = i *3;
 
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) }}};
 
1115
    }
 
1116
 
 
1117
    return 1;
 
1118
  },
 
1119
 
 
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)
 
1123
  },
 
1124
 
 
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)
 
1128
  },
 
1129
 
 
1130
  SDL_WM_GrabInput: function() {},
 
1131
  
 
1132
  SDL_WM_ToggleFullScreen: function(surf) {
 
1133
    if (Browser.isFullScreen) {
 
1134
      Module['canvas'].cancelFullScreen();
 
1135
      return 1;
 
1136
    } else {
 
1137
      return 0;
 
1138
    }
 
1139
  },
 
1140
 
 
1141
  // SDL_Image
 
1142
 
 
1143
  IMG_Init: function(flags) {
 
1144
    return flags; // We support JPG, PNG, TIF because browsers do
 
1145
  },
 
1146
 
 
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);
 
1153
    }
 
1154
    var raw = Module["preloadedImages"][filename];
 
1155
    if (!raw) {
 
1156
      if (raw === null) Module.printErr('Trying to reuse preloaded image, but freePreloadedMediaOnUse is set!');
 
1157
      Runtime.warnOnce('Cannot find preloaded image ' + filename);
 
1158
      return 0;
 
1159
    }
 
1160
    if (Module['freePreloadedMediaOnUse']) {
 
1161
      Module["preloadedImages"][filename] = null;
 
1162
    }
 
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
 
1174
    if (SDL.GL) {
 
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;
 
1177
    }
 
1178
    return surf;
 
1179
  },
 
1180
  SDL_LoadBMP: 'IMG_Load',
 
1181
  SDL_LoadBMP_RW: 'IMG_Load',
 
1182
  IMG_Load_RW: 'IMG_Load',
 
1183
 
 
1184
  // SDL_Audio
 
1185
 
 
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);
 
1189
 
 
1190
    // FIXME: Assumes 16-bit audio
 
1191
    assert(obtained === 0, 'Cannot return obtained SDL audio params');
 
1192
 
 
1193
    SDL.audio = {
 
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) }}},
 
1200
      paused: true,
 
1201
      timer: null
 
1202
    };
 
1203
 
 
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);
 
1210
    };
 
1211
    // Mozilla Audio API. TODO: Other audio APIs
 
1212
    try {
 
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?)
 
1220
        }
 
1221
        SDL.audio.mozOutput['mozWriteAudio'](mozBuffer);
 
1222
      }
 
1223
    } catch(e) {
 
1224
      SDL.audio = null;
 
1225
    }
 
1226
    if (!SDL.audio) return -1;
 
1227
    return 0;
 
1228
  },
 
1229
 
 
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);
 
1233
    }
 
1234
    SDL.audio.paused = pauseOn;
 
1235
  },
 
1236
 
 
1237
  SDL_CloseAudio__deps: ['SDL_PauseAudio', 'free'],
 
1238
  SDL_CloseAudio: function() {
 
1239
    if (SDL.audio) {
 
1240
      _SDL_PauseAudio(1);
 
1241
      _free(SDL.audio.buffer);
 
1242
      SDL.audio = null;
 
1243
    }
 
1244
  },
 
1245
 
 
1246
  SDL_LockAudio: function() {},
 
1247
  SDL_UnlockAudio: function() {},
 
1248
 
 
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() {},
 
1255
 
 
1256
  SDL_CreateCond: function() { return 0 },
 
1257
  SDL_CondSignal: function() {},
 
1258
  SDL_CondWait: function() {},
 
1259
  SDL_DestroyCond: function() {},
 
1260
 
 
1261
  SDL_StartTextInput: function() {
 
1262
    SDL.textInput = true;
 
1263
  },
 
1264
  SDL_StopTextInput: function() {
 
1265
    SDL.textInput = false;
 
1266
  },
 
1267
 
 
1268
  // SDL Mixer
 
1269
 
 
1270
  Mix_Init: function(flags) {
 
1271
    if (!flags) return 0;
 
1272
    return 8; /* MIX_INIT_OGG */
 
1273
  },
 
1274
  Mix_Quit: function(){},
 
1275
 
 
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;
 
1283
    return 0;
 
1284
  },
 
1285
 
 
1286
  Mix_CloseAudio: 'SDL_CloseAudio',
 
1287
 
 
1288
  Mix_AllocateChannels: function(num) {
 
1289
    SDL.allocateChannels(num);
 
1290
    return num;
 
1291
  },
 
1292
 
 
1293
  Mix_ChannelFinished: function(func) {
 
1294
    SDL.channelFinished = func;
 
1295
  },
 
1296
 
 
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);
 
1301
      }
 
1302
      return _Mix_Volume(SDL.numChannels-1, volume);
 
1303
    }
 
1304
    return SDL.setGetVolume(SDL.channels[channel], volume);
 
1305
  },
 
1306
 
 
1307
  Mix_SetPanning: function() {
 
1308
    return 0; // error
 
1309
  },
 
1310
 
 
1311
  Mix_LoadWAV_RW: function(filename, freesrc) {
 
1312
    filename = FS.standardizePath(Pointer_stringify(filename));
 
1313
    var raw = Module["preloadedAudios"][filename];
 
1314
    if (!raw) {
 
1315
      if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
 
1316
      Runtime.warnOnce('Cannot find preloaded audio ' + filename);
 
1317
      return 0;
 
1318
    }
 
1319
    if (Module['freePreloadedMediaOnUse']) {
 
1320
      Module["preloadedAudios"][filename] = null;
 
1321
    }
 
1322
    var id = SDL.audios.length;
 
1323
    // Keep the loaded audio in the audio arrays, ready for playback
 
1324
    SDL.audios.push({
 
1325
      source: filename,
 
1326
      audio: raw
 
1327
    });
 
1328
    return id;
 
1329
  },
 
1330
 
 
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?)
 
1340
    }
 
1341
    // FIXME: doesn't make sense to keep the audio element in the buffer
 
1342
    var id = SDL.audios.length;
 
1343
    SDL.audios.push({
 
1344
      source: '',
 
1345
      audio: audio,
 
1346
      buffer: buffer
 
1347
    });
 
1348
    return id;
 
1349
  },
 
1350
 
 
1351
  Mix_FreeChunk: function(id) {
 
1352
    SDL.audios[id] = null;
 
1353
  },
 
1354
  Mix_ReserveChannels: function(num) {
 
1355
    SDL.channelMinimumNumber = num;
 
1356
  },
 
1357
  Mix_PlayChannel: function(channel, id, loops) {
 
1358
    // TODO: handle loops
 
1359
 
 
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;
 
1365
 
 
1366
    // If the user asks us to allocate a channel automatically, get the first
 
1367
    // free one.
 
1368
    if (channel == -1) {
 
1369
      channel = SDL.channelMinimumNumber;
 
1370
      for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) {
 
1371
        if (!SDL.channels[i].audio) {
 
1372
          channel = i;
 
1373
          break;
 
1374
        }
 
1375
      }
 
1376
    }
 
1377
 
 
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);
 
1387
      }
 
1388
    }
 
1389
    // Either play the element, or load the dynamic data into it
 
1390
    if (info.buffer) {
 
1391
      var contextCtor = null;
 
1392
      if (audio && ('mozSetup' in audio)) { // Audio Data API
 
1393
        try {
 
1394
          audio['mozSetup'](audio.numChannels, audio.frequency);
 
1395
          audio["mozWriteAudio"](info.buffer);
 
1396
        } catch (e) {
 
1397
          // Workaround for Firefox bug 783052
 
1398
          // ignore this exception!
 
1399
        }
 
1400
      /*
 
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);
 
1414
          }
 
1415
          var remaining = info.buffer.length - currentIndex;
 
1416
          if (remaining > 2048) {
 
1417
            remaining = 2048;
 
1418
          }
 
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;
 
1422
            }
 
1423
            i += j;
 
1424
          }
 
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
 
1429
            }
 
1430
            i += j;
 
1431
          }
 
1432
        };
 
1433
        source.connect(jsNode);
 
1434
        jsNode.connect(context.destination);
 
1435
        source.noteOn(0);
 
1436
      */
 
1437
      }
 
1438
    } else {
 
1439
      audio.play();
 
1440
    }
 
1441
    audio.volume = channelInfo.volume;
 
1442
    audio.paused = false;
 
1443
    return channel;
 
1444
  },
 
1445
  Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
 
1446
 
 
1447
  Mix_FadingChannel: function(channel) {
 
1448
    return 0; // MIX_NO_FADING, TODO
 
1449
  },
 
1450
 
 
1451
  Mix_HaltChannel: function(channel) {
 
1452
    var info = SDL.channels[channel];
 
1453
    if (info.audio) {
 
1454
      info.audio.pause();
 
1455
      info.audio = null;
 
1456
    }
 
1457
    if (SDL.channelFinished) {
 
1458
      Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
 
1459
    }
 
1460
    return 0;
 
1461
  },
 
1462
 
 
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;
 
1468
    }
 
1469
  },
 
1470
 
 
1471
  Mix_VolumeMusic: function(volume) {
 
1472
    return SDL.setGetVolume(SDL.music, volume);
 
1473
  },
 
1474
 
 
1475
  Mix_LoadMUS: 'Mix_LoadWAV_RW',
 
1476
  Mix_LoadMUS_RW: 'Mix_LoadWAV_RW',
 
1477
 
 
1478
  Mix_FreeMusic: 'Mix_FreeChunk',
 
1479
 
 
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);
 
1488
    } else {
 
1489
      audio.play();
 
1490
    }
 
1491
    audio.volume = SDL.music.volume;
 
1492
    audio['onended'] = _Mix_HaltMusic; // will send callback
 
1493
    SDL.music.audio = audio;
 
1494
    return 0;
 
1495
  },
 
1496
 
 
1497
  Mix_PauseMusic: function() {
 
1498
    var audio = SDL.music.audio;
 
1499
    if (!audio) return 0;
 
1500
    audio.pause();
 
1501
    return 0;
 
1502
  },
 
1503
 
 
1504
  Mix_ResumeMusic: function() {
 
1505
    var audio = SDL.music.audio;
 
1506
    if (!audio) return 0;
 
1507
    audio.play();
 
1508
    return 0;
 
1509
  },
 
1510
 
 
1511
  Mix_HaltMusic: function() {
 
1512
    var audio = SDL.music.audio;
 
1513
    if (!audio) return 0;
 
1514
    audio.src = audio.src; // rewind
 
1515
    audio.pause();
 
1516
    SDL.music.audio = null;
 
1517
    if (SDL.hookMusicFinished) {
 
1518
      Runtime.dynCall('v', SDL.hookMusicFinished);
 
1519
    }
 
1520
    return 0;
 
1521
  },
 
1522
 
 
1523
  Mix_FadeInMusicPos: 'Mix_PlayMusic', // XXX ignore fading in effect
 
1524
 
 
1525
  Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect
 
1526
 
 
1527
  Mix_PlayingMusic: function() {
 
1528
    return (SDL.music.audio && !SDL.music.audio.paused) ? 1 : 0;
 
1529
  },
 
1530
 
 
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) {
 
1535
      var count = 0;
 
1536
      for (var i = 0; i < SDL.channels.length; i++) {
 
1537
        count += _Mix_Playing(i);
 
1538
      }
 
1539
      return count;
 
1540
    }
 
1541
    var info = SDL.channels[channel];
 
1542
    if (info && info.audio && !info.audio.paused) {
 
1543
      return 1;
 
1544
    }
 
1545
    return 0;
 
1546
  },
 
1547
  
 
1548
  Mix_Pause: function(channel) {
 
1549
    if (channel === -1) {
 
1550
      for (var i = 0; i<SDL.channels.length;i++) {
 
1551
        _Mix_Pause(i);
 
1552
      }
 
1553
      return;
 
1554
    }
 
1555
    var info = SDL.channels[channel];
 
1556
    if (info && info.audio) {
 
1557
      info.audio.pause();
 
1558
      info.audio.paused = true;
 
1559
    }
 
1560
  },
 
1561
  
 
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);
 
1568
      }
 
1569
      return pausedCount;
 
1570
    }
 
1571
    var info = SDL.channels[channel];
 
1572
    if (info && info.audio && info.audio.paused) {
 
1573
      return 1;
 
1574
    }
 
1575
    return 0;
 
1576
  },
 
1577
 
 
1578
  Mix_PausedMusic: function() {
 
1579
    return (SDL.music.audio && SDL.music.audio.paused) ? 1 : 0;
 
1580
  },
 
1581
 
 
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++) {
 
1586
        _Mix_Resume(i);
 
1587
      }
 
1588
      return;
 
1589
    }
 
1590
    var info = SDL.channels[channel];
 
1591
    if (info && info.audio) {
 
1592
      info.audio.play();
 
1593
    }
 
1594
  },
 
1595
 
 
1596
  // SDL TTF
 
1597
 
 
1598
  TTF_Init: function() { return 0 },
 
1599
 
 
1600
  TTF_OpenFont: function(filename, size) {
 
1601
    filename = FS.standardizePath(Pointer_stringify(filename));
 
1602
    var id = SDL.fonts.length;
 
1603
    SDL.fonts.push({
 
1604
      name: filename, // but we don't actually do anything with it..
 
1605
      size: size
 
1606
    });
 
1607
    return id;
 
1608
  },
 
1609
 
 
1610
  TTF_CloseFont: function(font) {
 
1611
    SDL.fonts[font] = null;
 
1612
  },
 
1613
 
 
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();
 
1630
    return surf;
 
1631
  },
 
1632
  TTF_RenderText_Blended: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
 
1633
  TTF_RenderText_Shaded: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
 
1634
 
 
1635
  TTF_SizeText: function(font, text, w, h) {
 
1636
    var fontData = SDL.fonts[font];
 
1637
    if (w) {
 
1638
      {{{ makeSetValue('w', '0', 'SDL.estimateTextWidth(fontData, Pointer_stringify(text))', 'i32') }}};
 
1639
    }
 
1640
    if (h) {
 
1641
      {{{ makeSetValue('h', '0', 'fontData.size', 'i32') }}};
 
1642
    }
 
1643
    return 0;
 
1644
  },
 
1645
 
 
1646
  TTF_FontAscent: function(font) {
 
1647
    var fontData = SDL.fonts[font];
 
1648
    return Math.floor(fontData.size*0.98); // XXX
 
1649
  },
 
1650
 
 
1651
  TTF_FontDescent: function(font) {
 
1652
    var fontData = SDL.fonts[font];
 
1653
    return Math.floor(fontData.size*0.02); // XXX
 
1654
  },
 
1655
 
 
1656
  // SDL gfx
 
1657
 
 
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();
 
1666
  },
 
1667
 
 
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();
 
1675
  },
 
1676
 
 
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();
 
1687
  },
 
1688
 
 
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);
 
1693
  },
 
1694
 
 
1695
  // GL
 
1696
 
 
1697
  SDL_GL_SetAttribute: function(attr, value) {
 
1698
    console.log('TODO: SDL_GL_SetAttribute');
 
1699
  },
 
1700
 
 
1701
  SDL_GL_GetProcAddress__deps: ['$GLEmulation'],
 
1702
  SDL_GL_GetProcAddress: function(name_) {
 
1703
    return GLEmulation.getProcAddress(Pointer_stringify(name_));
 
1704
  },
 
1705
 
 
1706
  SDL_GL_SwapBuffers: function() {},
 
1707
 
 
1708
  // TODO
 
1709
 
 
1710
  SDL_SetGamma: function(r, g, b) {
 
1711
    return -1;
 
1712
  },
 
1713
 
 
1714
  // Misc
 
1715
 
 
1716
  SDL_InitSubSystem: function(flags) { return 0 },
 
1717
 
 
1718
  SDL_NumJoysticks: function() { return 0 },
 
1719
 
 
1720
  SDL_RWFromFile: function(filename, mode) {
 
1721
    return filename; // XXX We just forward the filename
 
1722
  },
 
1723
 
 
1724
  SDL_EnableUNICODE: function(on) {
 
1725
    var ret = SDL.unicode || 0;
 
1726
    SDL.unicode = on;
 
1727
    return ret;
 
1728
  },
 
1729
 
 
1730
  SDL_AddTimer: function(interval, callback, param) {
 
1731
    return window.setTimeout(function() {
 
1732
      Runtime.dynCall('ii', callback, [interval, param]);
 
1733
    }, interval);
 
1734
  },
 
1735
  SDL_RemoveTimer: function(id) {
 
1736
    window.clearTimeout(id);
 
1737
    return true;
 
1738
  },
 
1739
 
 
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.'
 
1742
  },
 
1743
 
 
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' },
 
1752
 
 
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' },
 
1757
 
 
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' },
 
1761
};
 
1762
 
 
1763
autoAddDeps(LibrarySDL, '$SDL');
 
1764
mergeInto(LibraryManager.library, LibrarySDL);
 
1765