~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-09-20 22:44:35 UTC
  • mfrom: (4.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20130920224435-apuwj4fsl3fqv1a6
Tags: 1.5.6~20130920~6010666-1
* New snapshot release
* Update the list of supported architectures to the same as libv8
  (Closes: #723129)
* emlibtool has been removed from upstream.
* Fix warning syntax-error-in-dep5-copyright
* Refresh of the patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
//                   or otherwise).
11
11
 
12
12
var LibrarySDL = {
13
 
  $SDL__deps: ['$FS', '$Browser'],
 
13
  $SDL__deps: ['$FS', '$PATH', '$Browser'],
14
14
  $SDL: {
15
15
    defaults: {
16
16
      width: 320,
21
21
    version: null,
22
22
 
23
23
    surfaces: {},
 
24
    // A pool of freed canvas elements. Reusing them avoids GC pauses.
 
25
    canvasPool: [],
24
26
    events: [],
25
27
    fonts: [null],
26
28
 
27
29
    // The currently preloaded audio elements ready to be played
28
30
    audios: [null],
 
31
    rwops: [null],
29
32
    // The currently playing audio element.  There's only one music track.
30
33
    music: {
31
34
      audio: null,
43
46
    keyboardState: null,
44
47
    keyboardMap: {},
45
48
 
 
49
    canRequestFullscreen: false,
 
50
    isRequestingFullscreen: false,
 
51
 
46
52
    textInput: false,
47
53
 
48
54
    startTime: null,
242
248
    },
243
249
 
244
250
    translateColorToCSSRGBA: function(rgba) {
245
 
      return 'rgba(' + ((rgba >> 24)&255) + ',' + ((rgba >> 16)&255) + ',' + ((rgba >> 8)&255) + ',' + ((rgba&255)/255) + ')';
 
251
      return 'rgba(' + (rgba&0xff) + ',' + (rgba>>8 & 0xff) + ',' + (rgba>>16 & 0xff) + ',' + (rgba>>>24)/0xff + ')';
246
252
    },
247
253
 
248
254
    translateRGBAToCSSRGBA: function(r, g, b, a) {
249
 
      return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')';
 
255
      return 'rgba(' + (r&0xff) + ',' + (g&0xff) + ',' + (b&0xff) + ',' + (a&0xff)/255 + ')';
250
256
    },
251
257
 
252
258
    translateRGBAToColor: function(r, g, b, a) {
253
 
      return (r << 24) + (g << 16) + (b << 8) + a;
 
259
      return r | g << 8 | b << 16 | a << 24;
254
260
    },
255
261
 
256
262
    makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) {
257
263
      flags = flags || 0;
258
 
      var surf = _malloc(14*Runtime.QUANTUM_SIZE);  // SDL_Surface has 14 fields of quantum size
 
264
      var surf = _malloc(15*Runtime.QUANTUM_SIZE);  // SDL_Surface has 15 fields of quantum size
259
265
      var buffer = _malloc(width*height*4); // TODO: only allocate when locked the first time
260
266
      var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE);
261
267
      flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked
273
279
      {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*5', '0', 'buffer', 'void*') }}}      // SDL_Surface.pixels
274
280
      {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*6', '0', '0', 'i32*') }}}      // SDL_Surface.offset
275
281
 
 
282
      {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*14', '0', '1', 'i32') }}}
 
283
 
276
284
      {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.format', '0', '-2042224636', 'i32') }}} // SDL_PIXELFORMAT_RGBA8888
277
285
      {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.palette', '0', '0', 'i32') }}} // TODO
278
286
      {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', 'bpp * 8', 'i8') }}}
288
296
      SDL.GL = SDL.GL || useWebGL;
289
297
      var canvas;
290
298
      if (!usePageCanvas) {
291
 
        canvas = document.createElement('canvas');
 
299
        if (SDL.canvasPool.length > 0) {
 
300
          canvas = SDL.canvasPool.pop();
 
301
        } else {
 
302
          canvas = document.createElement('canvas');
 
303
        }
292
304
        canvas.width = width;
293
305
        canvas.height = height;
294
306
      } else {
309
321
        usePageCanvas: usePageCanvas,
310
322
        source: source,
311
323
 
312
 
        isFlagSet: function (flag) {
 
324
        isFlagSet: function(flag) {
313
325
          return flags & flag;
314
326
        }
315
327
      };
355
367
    },
356
368
 
357
369
    freeSurface: function(surf) {
358
 
      _free(SDL.surfaces[surf].buffer);
359
 
      _free(SDL.surfaces[surf].pixelFormat);
 
370
      var refcountPointer = surf + Runtime.QUANTUM_SIZE * 14;
 
371
      var refcount = {{{ makeGetValue('refcountPointer', '0', 'i32') }}};
 
372
      if (refcount > 1) {
 
373
        {{{ makeSetValue('refcountPointer', '0', 'refcount - 1', 'i32') }}};
 
374
        return;
 
375
      }
 
376
 
 
377
      var info = SDL.surfaces[surf];
 
378
      if (!info.usePageCanvas && info.canvas) SDL.canvasPool.push(info.canvas);
 
379
      _free(info.buffer);
 
380
      _free(info.pixelFormat);
360
381
      _free(surf);
361
382
      SDL.surfaces[surf] = null;
362
383
    },
363
384
 
 
385
    touchX: 0, touchY: 0,
 
386
    savedKeydown: null,
 
387
 
364
388
    receiveEvent: function(event) {
365
389
      switch(event.type) {
 
390
        case 'touchstart':
 
391
          event.preventDefault();
 
392
          var touch = event.touches[0];
 
393
          touchX = touch.pageX;
 
394
          touchY = touch.pageY;
 
395
          var event = {
 
396
            type: 'mousedown',
 
397
            button: 0,
 
398
            pageX: touchX,
 
399
            pageY: touchY
 
400
          };
 
401
          SDL.DOMButtons[0] = 1;
 
402
          SDL.events.push(event);
 
403
          break;
 
404
        case 'touchmove':
 
405
          event.preventDefault();
 
406
          var touch = event.touches[0];
 
407
          touchX = touch.pageX;
 
408
          touchY = touch.pageY;
 
409
          event = {
 
410
            type: 'mousemove',
 
411
            button: 0,
 
412
            pageX: touchX,
 
413
            pageY: touchY
 
414
          };
 
415
          SDL.events.push(event);
 
416
          break;
 
417
        case 'touchend':
 
418
          event.preventDefault();
 
419
          event = {
 
420
            type: 'mouseup',
 
421
            button: 0,
 
422
            pageX: touchX,
 
423
            pageY: touchY
 
424
          };
 
425
          SDL.DOMButtons[0] = 0;
 
426
          SDL.events.push(event);
 
427
          break;
366
428
        case 'mousemove':
367
429
          if (Browser.pointerLock) {
368
430
            // workaround for firefox bug 750111
380
442
          }
381
443
          // fall through
382
444
        case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
 
445
          // If we preventDefault on keydown events, the subsequent keypress events
 
446
          // won't fire. However, it's fine (and in some cases necessary) to
 
447
          // preventDefault for keys that don't generate a character. Otherwise,
 
448
          // preventDefault is the right thing to do in general.
 
449
          if (event.type !== 'keydown' || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) {
 
450
            event.preventDefault();
 
451
          }
 
452
 
383
453
          if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') {
384
454
            var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3;
385
455
            var event2 = {
401
471
            // ignore extra ups, can happen if we leave the canvas while pressing down, then return,
402
472
            // since we add a mouseup in that case
403
473
            if (!SDL.DOMButtons[event.button]) {
404
 
              event.preventDefault();
405
474
              return;
406
475
            }
407
476
 
408
477
            SDL.DOMButtons[event.button] = 0;
409
478
          }
410
479
 
411
 
          if (event.type == 'keypress' && !SDL.textInput) {
412
 
            break;
413
 
          }
414
 
          
415
 
          SDL.events.push(event);
 
480
          // We can only request fullscreen as the result of user input.
 
481
          // Due to this limitation, we toggle a boolean on keydown which
 
482
          // SDL_WM_ToggleFullScreen will check and subsequently set another
 
483
          // flag indicating for us to request fullscreen on the following
 
484
          // keyup. This isn't perfect, but it enables SDL_WM_ToggleFullScreen
 
485
          // to work as the result of a keypress (which is an extremely
 
486
          // common use case).
 
487
          if (event.type === 'keydown') {
 
488
            SDL.canRequestFullscreen = true;
 
489
          } else if (event.type === 'keyup') {
 
490
            if (SDL.isRequestingFullscreen) {
 
491
              Module['requestFullScreen'](true, true);
 
492
              SDL.isRequestingFullscreen = false;
 
493
            }
 
494
            SDL.canRequestFullscreen = false;
 
495
          }
 
496
 
 
497
          // SDL expects a unicode character to be passed to its keydown events.
 
498
          // Unfortunately, the browser APIs only provide a charCode property on
 
499
          // keypress events, so we must backfill in keydown events with their
 
500
          // subsequent keypress event's charCode.
 
501
          if (event.type === 'keypress' && SDL.savedKeydown) {
 
502
            // charCode is read-only
 
503
            SDL.savedKeydown.keypressCharCode = event.charCode;
 
504
            SDL.savedKeydown = null;
 
505
          } else if (event.type === 'keydown') {
 
506
            SDL.savedKeydown = event;
 
507
          }
 
508
 
 
509
          // Don't push keypress events unless SDL_StartTextInput has been called.
 
510
          if (event.type !== 'keypress' || SDL.textInput) {
 
511
            SDL.events.push(event);
 
512
          }
416
513
          break;
417
514
        case 'mouseout':
418
515
          // Un-press all pressed mouse buttons, because we might miss the release outside of the canvas
427
524
              SDL.DOMButtons[i] = 0;
428
525
            }
429
526
          }
 
527
          event.preventDefault();
430
528
          break;
431
529
        case 'blur':
432
530
        case 'visibilitychange': {
437
535
              keyCode: SDL.keyboardMap[code]
438
536
            });
439
537
          }
 
538
          event.preventDefault();
440
539
          break;
441
540
        }
442
541
        case 'unload':
448
547
          return;
449
548
        case 'resize':
450
549
          SDL.events.push(event);
 
550
          // manually triggered resize event doesn't have a preventDefault member
 
551
          if (event.preventDefault) {
 
552
            event.preventDefault();
 
553
          }
451
554
          break;
452
555
      }
453
556
      if (SDL.events.length >= 10000) {
454
557
        Module.printErr('SDL event queue full, dropping events');
455
558
        SDL.events = SDL.events.slice(0, 10000);
456
559
      }
457
 
      // manually triggered resize event doesn't have a preventDefault member
458
 
      if (event.preventDefault) {
459
 
        event.preventDefault();
460
 
      }
461
560
      return;
462
561
    },
463
562
 
 
563
    handleEvent: function(event) {
 
564
      if (event.handled) return;
 
565
      event.handled = true;
 
566
 
 
567
      switch (event.type) {
 
568
        case 'keydown': case 'keyup': {
 
569
          var down = event.type === 'keydown';
 
570
          var code = event.keyCode;
 
571
          if (code >= 65 && code <= 90) {
 
572
            code += 32; // make lowercase for SDL
 
573
          } else {
 
574
            code = SDL.keyCodes[event.keyCode] || event.keyCode;
 
575
          }
 
576
 
 
577
          {{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}};
 
578
          // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED
 
579
          SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL
 
580
            ({{{ makeGetValue('SDL.keyboardState', '1249', 'i8') }}} ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT
 
581
            ({{{ makeGetValue('SDL.keyboardState', '1250', 'i8') }}} ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT
 
582
 
 
583
          if (down) {
 
584
            SDL.keyboardMap[code] = event.keyCode; // save the DOM input, which we can use to unpress it during blur
 
585
          } else {
 
586
            delete SDL.keyboardMap[code];
 
587
          }
 
588
 
 
589
          break;
 
590
        }
 
591
        case 'mousedown': case 'mouseup':
 
592
          if (event.type == 'mousedown') {
 
593
            // SDL_BUTTON(x) is defined as (1 << ((x)-1)).  SDL buttons are 1-3,
 
594
            // and DOM buttons are 0-2, so this means that the below formula is
 
595
            // correct.
 
596
            SDL.buttonState |= 1 << event.button;
 
597
          } else if (event.type == 'mouseup') {
 
598
            SDL.buttonState &= ~(1 << event.button);
 
599
          }
 
600
          // fall through
 
601
        case 'mousemove': {
 
602
          Browser.calculateMouseEvent(event);
 
603
          break;
 
604
        }
 
605
      }
 
606
    },
 
607
 
464
608
    makeCEvent: function(event, ptr) {
465
609
      if (typeof event === 'number') {
466
610
        // This is a pointer to a native C event that was SDL_PushEvent'ed
468
612
        return;
469
613
      }
470
614
 
471
 
      switch(event.type) {
 
615
      SDL.handleEvent(event);
 
616
 
 
617
      switch (event.type) {
472
618
        case 'keydown': case 'keyup': {
473
619
          var down = event.type === 'keydown';
474
620
          //Module.print('Received key event: ' + event.keyCode);
485
631
            scan = SDL.scanCodes[key] || key;
486
632
          }
487
633
 
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
 
          // TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED
497
 
          SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL
498
 
            ({{{ makeGetValue('SDL.keyboardState', '1249', 'i8') }}} ? 0x0001 | 0x0002 : 0) | // KMOD_LSHIFT & KMOD_RSHIFT
499
 
            ({{{ makeGetValue('SDL.keyboardState', '1250', 'i8') }}} ? 0x0100 | 0x0200 : 0); // KMOD_LALT & KMOD_RALT
500
 
 
501
634
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}
502
635
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.state', 'down ? 1 : 0', 'i8') }}}
503
636
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.repeat', '0', 'i8') }}} // TODO
504
637
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.scancode', 'scan', 'i32') }}}
505
638
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.sym', 'key', 'i32') }}}
506
 
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', 'SDL.modState', 'i32') }}}
507
 
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'key', 'i32') }}}
 
639
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.mod', 'SDL.modState', 'i16') }}}
 
640
          // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode.
 
641
          {{{ makeSetValue('ptr', 'SDL.structs.KeyboardEvent.keysym + SDL.structs.keysym.unicode', 'event.keypressCharCode || key', 'i32') }}}
508
642
 
509
643
          break;
510
644
        }
517
651
          }
518
652
          break;
519
653
        }
520
 
        case 'mousedown': case 'mouseup':
521
 
          if (event.type == 'mousedown') {
522
 
            // SDL_BUTTON(x) is defined as (1 << ((x)-1)).  SDL buttons are 1-3,
523
 
            // and DOM buttons are 0-2, so this means that the below formula is
524
 
            // correct.
525
 
            SDL.buttonState |= 1 << event.button;
526
 
          } else if (event.type == 'mouseup') {
527
 
            SDL.buttonState &= ~(1 << event.button);
528
 
          }
529
 
          // fall through
530
 
        case 'mousemove': {
531
 
          Browser.calculateMouseEvent(event);
 
654
        case 'mousedown': case 'mouseup': case 'mousemove': {
532
655
          if (event.type != 'mousemove') {
533
656
            var down = event.type === 'mousedown';
534
657
            {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
632
755
      document.addEventListener("keydown", SDL.receiveEvent);
633
756
      document.addEventListener("keyup", SDL.receiveEvent);
634
757
      document.addEventListener("keypress", SDL.receiveEvent);
635
 
      document.addEventListener("blur", SDL.receiveEvent);
 
758
      window.addEventListener("blur", SDL.receiveEvent);
636
759
      document.addEventListener("visibilitychange", SDL.receiveEvent);
637
760
    }
638
761
    window.addEventListener("unload", SDL.receiveEvent);
679
802
    return depth; // all modes are ok.
680
803
  },
681
804
 
 
805
  SDL_AudioDriverName__deps: ['SDL_VideoDriverName'],
 
806
  SDL_AudioDriverName: function(buf, max_size) {
 
807
    return _SDL_VideoDriverName(buf, max_size);
 
808
  },
 
809
 
682
810
  SDL_VideoDriverName: function(buf, max_size) {
683
811
    if (SDL.startTime === null) {
684
812
      return 0; //return NULL
755
883
    surfData.locked++;
756
884
    if (surfData.locked > 1) return 0;
757
885
 
 
886
    // Mark in C/C++-accessible SDL structure
 
887
    // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ...
 
888
    // So we have fields all of the same size, and 5 of them before us.
 
889
    // TODO: Use macros like in library.js
 
890
    {{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}};
 
891
 
 
892
    if (surf == SDL.screen && Module.screenIsReadOnly && surfData.image) return 0;
 
893
 
758
894
    surfData.image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
759
895
    if (surf == SDL.screen) {
760
896
      var data = surfData.image.data;
797
933
      }
798
934
    }
799
935
 
800
 
    // Mark in C/C++-accessible SDL structure
801
 
    // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ...
802
 
    // So we have fields all of the same size, and 5 of them before us.
803
 
    // TODO: Use macros like in library.js
804
 
    {{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}};
805
 
 
806
936
    return 0;
807
937
  },
808
938
 
819
949
    if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) {
820
950
      SDL.copyIndexedColorData(surfData);
821
951
    } else if (!surfData.colors) {
822
 
      var num = surfData.image.data.length;
823
952
      var data = surfData.image.data;
824
953
      var buffer = surfData.buffer;
825
954
#if USE_TYPED_ARRAYS == 2
827
956
      var src = buffer >> 2;
828
957
      var dst = 0;
829
958
      var isScreen = surf == SDL.screen;
 
959
      var data32 = new Uint32Array(data.buffer);
 
960
      var num = data32.length;
830
961
      while (dst < num) {
831
 
        // TODO: access underlying data buffer and write in 32-bit chunks or more
832
 
        var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
833
 
        data[dst  ] = val & 0xff;
834
 
        data[dst+1] = (val >> 8) & 0xff;
835
 
        data[dst+2] = (val >> 16) & 0xff;
836
 
        data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
837
 
        src++;
838
 
        dst += 4;
 
962
        // HEAP32[src++] is an optimization. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
 
963
        data32[dst++] = HEAP32[src++] | (isScreen ? 0xff000000 : 0);
839
964
      }
840
965
#else
 
966
      var num = surfData.image.data.length;
841
967
      for (var i = 0; i < num; i++) {
842
968
        // We may need to correct signs here. Potentially you can hardcode a write of 255 to alpha, say, and
843
969
        // the compiler may decide to write -1 in the llvm bitcode...
895
1021
    // TODO
896
1022
  },
897
1023
 
898
 
  SDL_GetKeyboardState: function() {
 
1024
  SDL_GetKeyboardState: function(numKeys) {
 
1025
    if (numKeys) {
 
1026
      {{{ makeSetValue('numKeys', 0, 0x10000, 'i32') }}};
 
1027
    }
899
1028
    return SDL.keyboardState;
900
1029
  },
901
1030
 
965
1094
    return SDL.makeSurface(width, height, flags, false, 'CreateRGBSurface', rmask, gmask, bmask, amask);
966
1095
  },
967
1096
 
 
1097
  SDL_CreateRGBSurfaceFrom: function(pixels, width, height, depth, pitch, rmask, gmask, bmask, amask) {
 
1098
    // TODO: Actually fill pixel data to created surface.
 
1099
    // TODO: Take into account depth and pitch parameters.
 
1100
    console.log('TODO: Partially unimplemented SDL_CreateRGBSurfaceFrom called!');
 
1101
    return SDL.makeSurface(width, height, 0, false, 'CreateRGBSurfaceFrom', rmask, gmask, bmask, amask);
 
1102
  },
 
1103
 
968
1104
  SDL_DisplayFormatAlpha: function(surf) {
969
1105
    var oldData = SDL.surfaces[surf];
970
1106
    var ret = SDL.makeSurface(oldData.width, oldData.height, oldData.flags, false, 'copy:' + oldData.source);
1019
1155
    surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color);
1020
1156
    surfData.ctx.fillRect(r.x, r.y, r.w, r.h);
1021
1157
    surfData.ctx.restore();
 
1158
    return 0;
1022
1159
  },
1023
1160
 
1024
1161
  SDL_BlitSurface__deps: ['SDL_UpperBlit'],
1028
1165
 
1029
1166
  zoomSurface: function(src, x, y, smooth) {
1030
1167
    var srcData = SDL.surfaces[src];
1031
 
    var w = srcData.width*x;
1032
 
    var h = srcData.height*y;
1033
 
    var ret = SDL.makeSurface(w, h, srcData.flags, false, 'zoomSurface');
 
1168
    var w = srcData.width * x;
 
1169
    var h = srcData.height * y;
 
1170
    var ret = SDL.makeSurface(Math.abs(w), Math.abs(h), srcData.flags, false, 'zoomSurface');
1034
1171
    var dstData = SDL.surfaces[ret];
1035
 
    dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
 
1172
    if (x >= 0 && y >= 0) dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
 
1173
    else {
 
1174
      dstData.ctx.save();
 
1175
      dstData.ctx.scale(x < 0 ? -1 : 1, y < 0 ? -1 : 1);
 
1176
      dstData.ctx.drawImage(srcData.canvas, w < 0 ? w : 0, h < 0 ? h : 0, Math.abs(w), Math.abs(h));
 
1177
      // XXX I think this should work according to the spec, but currently
 
1178
      // fails on FF: dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h);
 
1179
      dstData.ctx.restore();
 
1180
    }
1036
1181
    return ret;
1037
1182
  },
1038
1183
 
 
1184
  rotozoomSurface__deps: ['zoomSurface'],
1039
1185
  rotozoomSurface: function(src, angle, zoom, smooth) {
 
1186
    if (angle % 360 === 0) {
 
1187
      return _zoomSurface(src, zoom, zoom, smooth);
 
1188
    }
1040
1189
    var srcData = SDL.surfaces[src];
1041
1190
    var w = srcData.width * zoom;
1042
1191
    var h = srcData.height * zoom;
1053
1202
    SDL.surfaces[surf].alpha = alpha;
1054
1203
  },
1055
1204
 
 
1205
  SDL_SetColorKey: function(surf, flag, key) {
 
1206
    // SetColorKey assigns one color to be rendered as transparent. I don't
 
1207
    // think the canvas API allows for anything like this, and iterating through
 
1208
    // each pixel to replace that color seems prohibitively expensive.
 
1209
    Runtime.warnOnce('SDL_SetColorKey is a no-op for performance reasons');
 
1210
    return 0;
 
1211
  },
 
1212
 
1056
1213
  SDL_GetTicks: function() {
1057
1214
    return Math.floor(Date.now() - SDL.startTime);
1058
1215
  },
1089
1246
    }
1090
1247
  },
1091
1248
 
1092
 
  SDL_PumpEvents: function(){},
 
1249
  SDL_PumpEvents: function(){
 
1250
    SDL.events.forEach(function(event) {
 
1251
      SDL.handleEvent(event);
 
1252
    });
 
1253
  },
1093
1254
 
1094
1255
  SDL_SetColors: function(surf, colors, firstColor, nColors) {
1095
1256
    var surfData = SDL.surfaces[surf];
1112
1273
    return 1;
1113
1274
  },
1114
1275
 
 
1276
  SDL_SetPalette__deps: ['SDL_SetColors'],
 
1277
  SDL_SetPalette: function(surf, flags, colors, firstColor, nColors) {
 
1278
    return _SDL_SetColors(surf, colors, firstColor, nColors);
 
1279
  },
 
1280
 
1115
1281
  SDL_MapRGB: function(fmt, r, g, b) {
1116
 
    // Canvas screens are always RGBA
1117
 
    return 0xff+((b&0xff)<<8)+((g&0xff)<<16)+((r&0xff)<<24)
 
1282
    // Canvas screens are always RGBA. We assume the machine is little-endian.
 
1283
    return r&0xff|(g&0xff)<<8|(b&0xff)<<16|0xff000000;
1118
1284
  },
1119
1285
 
1120
1286
  SDL_MapRGBA: function(fmt, r, g, b, a) {
1121
 
    // Canvas screens are always RGBA
1122
 
    return (a&0xff)+((b&0xff)<<8)+((g&0xff)<<16)+((r&0xff)<<24)
 
1287
    // Canvas screens are always RGBA. We assume the machine is little-endian.
 
1288
    return r&0xff|(g&0xff)<<8|(b&0xff)<<16|(a&0xff)<<24;
1123
1289
  },
1124
1290
 
1125
1291
  SDL_GetAppState: function() {
1143
1309
      Module['canvas'].cancelFullScreen();
1144
1310
      return 1;
1145
1311
    } else {
1146
 
      return 0;
 
1312
      if (!SDL.canRequestFullscreen) {
 
1313
        return 0;
 
1314
      }
 
1315
      SDL.isRequestingFullscreen = true;
 
1316
      return 1;
1147
1317
    }
1148
1318
  },
1149
1319
 
1153
1323
    return flags; // We support JPG, PNG, TIF because browsers do
1154
1324
  },
1155
1325
 
1156
 
  IMG_Load__deps: ['SDL_LockSurface'],
1157
 
  IMG_Load: function(filename) {
1158
 
    filename = FS.standardizePath(Pointer_stringify(filename));
1159
 
    if (filename[0] == '/') {
1160
 
      // Convert the path to relative
1161
 
      filename = filename.substr(1);
1162
 
    }
1163
 
    var raw = Module["preloadedImages"][filename];
1164
 
    if (!raw) {
1165
 
      if (raw === null) Module.printErr('Trying to reuse preloaded image, but freePreloadedMediaOnUse is set!');
1166
 
      Runtime.warnOnce('Cannot find preloaded image ' + filename);
1167
 
      return 0;
1168
 
    }
1169
 
    if (Module['freePreloadedMediaOnUse']) {
1170
 
      Module["preloadedImages"][filename] = null;
1171
 
    }
1172
 
    var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
1173
 
    var surfData = SDL.surfaces[surf];
1174
 
    surfData.ctx.globalCompositeOperation = "copy";
1175
 
    surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height);
1176
 
    surfData.ctx.globalCompositeOperation = "source-over";
1177
 
    // XXX SDL does not specify that loaded images must have available pixel data, in fact
1178
 
    //     there are cases where you just want to blit them, so you just need the hardware
1179
 
    //     accelerated version. However, code everywhere seems to assume that the pixels
1180
 
    //     are in fact available, so we retrieve it here. This does add overhead though.
1181
 
    _SDL_LockSurface(surf);
1182
 
    surfData.locked--; // The surface is not actually locked in this hack
1183
 
    if (SDL.GL) {
1184
 
      // After getting the pixel data, we can free the canvas and context if we do not need to do 2D canvas blitting
1185
 
      surfData.canvas = surfData.ctx = null;
1186
 
    }
1187
 
    return surf;
 
1326
  IMG_Load_RW__deps: ['SDL_LockSurface', 'SDL_FreeRW'],
 
1327
  IMG_Load_RW: function(rwopsID, freeSrc) {
 
1328
    try {
 
1329
      // stb_image integration support
 
1330
      var cleanup = function() {
 
1331
        if (rwops && freeSrc) _SDL_FreeRW(rwopsID);
 
1332
      };
 
1333
      function addCleanup(func) {
 
1334
        var old = cleanup;
 
1335
        cleanup = function() {
 
1336
          old();
 
1337
          func();
 
1338
        }
 
1339
      }
 
1340
      function callStbImage(func, params) {
 
1341
        var x = Module['_malloc']({{{ QUANTUM_SIZE }}});
 
1342
        var y = Module['_malloc']({{{ QUANTUM_SIZE }}});
 
1343
        var comp = Module['_malloc']({{{ QUANTUM_SIZE }}});
 
1344
        addCleanup(function() {
 
1345
          Module['_free'](x);
 
1346
          Module['_free'](y);
 
1347
          Module['_free'](comp);
 
1348
          if (data) Module['_stbi_image_free'](data);
 
1349
        });
 
1350
        var data = Module['_' + func].apply(null, params.concat([x, y, comp, 0]));
 
1351
        if (!data) return null;
 
1352
        return {
 
1353
          rawData: true,
 
1354
          data: data,
 
1355
          width: {{{ makeGetValue('x', 0, 'i32') }}},
 
1356
          height: {{{ makeGetValue('y', 0, 'i32') }}},
 
1357
          size: {{{ makeGetValue('x', 0, 'i32') }}} * {{{ makeGetValue('y', 0, 'i32') }}} * {{{ makeGetValue('comp', 0, 'i32') }}},
 
1358
          bpp: {{{ makeGetValue('comp', 0, 'i32') }}}
 
1359
        };
 
1360
      }
 
1361
 
 
1362
      var rwops = SDL.rwops[rwopsID];
 
1363
      if (rwops === undefined) {
 
1364
        return 0;
 
1365
      }
 
1366
 
 
1367
      var filename = rwops.filename;
 
1368
      if (filename === undefined) {
 
1369
#if STB_IMAGE
 
1370
        var raw = callStbImage('stbi_load_from_memory', [rwops.bytes, rwops.count]);
 
1371
        if (!raw) return 0;
 
1372
#else
 
1373
        Runtime.warnOnce('Only file names that have been preloaded are supported for IMG_Load_RW. Consider using STB_IMAGE=1 if you want synchronous image decoding (see settings.js)');
 
1374
        return 0;
 
1375
#endif
 
1376
      }
 
1377
 
 
1378
      if (!raw) {
 
1379
        filename = PATH.resolve(filename);
 
1380
        var raw = Module["preloadedImages"][filename];
 
1381
        if (!raw) {
 
1382
          if (raw === null) Module.printErr('Trying to reuse preloaded image, but freePreloadedMediaOnUse is set!');
 
1383
#if STB_IMAGE
 
1384
          var name = Module['_malloc'](filename.length+1);
 
1385
          writeStringToMemory(filename, name);
 
1386
          addCleanup(function() {
 
1387
            Module['_free'](name);
 
1388
          });
 
1389
          var raw = callStbImage('stbi_load', [name]);
 
1390
          if (!raw) return 0;
 
1391
#else
 
1392
          Runtime.warnOnce('Cannot find preloaded image ' + filename);
 
1393
          Runtime.warnOnce('Cannot find preloaded image ' + filename + '. Consider using STB_IMAGE=1 if you want synchronous image decoding (see settings.js)');
 
1394
          return 0;
 
1395
#endif
 
1396
        } else if (Module['freePreloadedMediaOnUse']) {
 
1397
          Module["preloadedImages"][filename] = null;
 
1398
        }
 
1399
      }
 
1400
 
 
1401
      var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename);
 
1402
      var surfData = SDL.surfaces[surf];
 
1403
      surfData.ctx.globalCompositeOperation = "copy";
 
1404
      if (!raw.rawData) {
 
1405
        surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height);
 
1406
      } else {
 
1407
        var imageData = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
 
1408
        if (raw.bpp == 4) {
 
1409
          imageData.data.set({{{ makeHEAPView('U8', 'raw.data', 'raw.data+raw.size') }}});
 
1410
        } else if (raw.bpp == 3) {
 
1411
          var pixels = raw.size/3;
 
1412
          var data = imageData.data;
 
1413
          var sourcePtr = raw.data;
 
1414
          var destPtr = 0;
 
1415
          for (var i = 0; i < pixels; i++) {
 
1416
            data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}};
 
1417
            data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}};
 
1418
            data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}};
 
1419
            data[destPtr++] = 255;
 
1420
          }
 
1421
        } else {
 
1422
          Module.printErr('cannot handle bpp ' + raw.bpp);
 
1423
          return 0;
 
1424
        }
 
1425
        surfData.ctx.putImageData(imageData, 0, 0);
 
1426
      }
 
1427
      surfData.ctx.globalCompositeOperation = "source-over";
 
1428
      // XXX SDL does not specify that loaded images must have available pixel data, in fact
 
1429
      //     there are cases where you just want to blit them, so you just need the hardware
 
1430
      //     accelerated version. However, code everywhere seems to assume that the pixels
 
1431
      //     are in fact available, so we retrieve it here. This does add overhead though.
 
1432
      _SDL_LockSurface(surf);
 
1433
      surfData.locked--; // The surface is not actually locked in this hack
 
1434
      if (SDL.GL) {
 
1435
        // After getting the pixel data, we can free the canvas and context if we do not need to do 2D canvas blitting
 
1436
        surfData.canvas = surfData.ctx = null;
 
1437
      }
 
1438
      return surf;
 
1439
    } finally {
 
1440
      cleanup();
 
1441
    }
1188
1442
  },
1189
1443
  SDL_LoadBMP: 'IMG_Load',
1190
 
  SDL_LoadBMP_RW: 'IMG_Load',
1191
 
  IMG_Load_RW: 'IMG_Load',
 
1444
  SDL_LoadBMP_RW: 'IMG_Load_RW',
 
1445
  IMG_Load__deps: ['IMG_Load_RW', 'SDL_RWFromFile'],
 
1446
  IMG_Load: function(filename){
 
1447
    var rwops = _SDL_RWFromFile(filename);
 
1448
    var result = _IMG_Load_RW(rwops, 1);
 
1449
    return result;
 
1450
  },
1192
1451
 
1193
1452
  // SDL_Audio
1194
1453
 
1324
1583
    return 0; // error
1325
1584
  },
1326
1585
 
1327
 
  Mix_LoadWAV_RW: function(filename, freesrc) {
1328
 
    filename = FS.standardizePath(Pointer_stringify(filename));
1329
 
    var raw = Module["preloadedAudios"][filename];
1330
 
    if (!raw) {
1331
 
      if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
1332
 
      Runtime.warnOnce('Cannot find preloaded audio ' + filename);
1333
 
      return 0;
1334
 
    }
1335
 
    if (Module['freePreloadedMediaOnUse']) {
1336
 
      Module["preloadedAudios"][filename] = null;
1337
 
    }
 
1586
  Mix_LoadWAV_RW: function(rwopsID, freesrc) {
 
1587
    var rwops = SDL.rwops[rwopsID];
 
1588
 
 
1589
    if (rwops === undefined)
 
1590
      return 0;
 
1591
 
 
1592
    var filename = '';
 
1593
    var audio;
 
1594
    var bytes;
 
1595
    
 
1596
    if (rwops.filename !== undefined) {
 
1597
      filename = PATH.resolve(rwops.filename);
 
1598
      var raw = Module["preloadedAudios"][filename];
 
1599
      if (!raw) {
 
1600
        if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
 
1601
        Runtime.warnOnce('Cannot find preloaded audio ' + filename);
 
1602
        
 
1603
        // see if we can read the file-contents from the in-memory FS
 
1604
        var fileObject = FS.findObject(filename);
 
1605
        
 
1606
        if (fileObject === null) Module.printErr('Couldn\'t find file for: ' + filename);
 
1607
        
 
1608
        // We found the file. Load the contents
 
1609
        if (fileObject && !fileObject.isFolder && fileObject.read) {
 
1610
          bytes = fileObject.contents;
 
1611
        } else {
 
1612
          return 0;
 
1613
        }
 
1614
      }
 
1615
      if (Module['freePreloadedMediaOnUse']) {
 
1616
        Module["preloadedAudios"][filename] = null;
 
1617
      }
 
1618
      audio = raw;
 
1619
    }
 
1620
    else if (rwops.bytes !== undefined) {
 
1621
      bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count);
 
1622
    }
 
1623
    else {
 
1624
      return 0;
 
1625
    }
 
1626
    
 
1627
    // Here, we didn't find a preloaded audio but we either were passed a filepath for
 
1628
    // which we loaded bytes, or we were passed some bytes
 
1629
    if (audio === undefined && bytes) {
 
1630
      var blob = new Blob([new Uint8Array(bytes)], {type: rwops.mimetype});
 
1631
      var url = URL.createObjectURL(blob);
 
1632
      audio = new Audio();
 
1633
      audio.src = url;
 
1634
    }
 
1635
    
1338
1636
    var id = SDL.audios.length;
1339
1637
    // Keep the loaded audio in the audio arrays, ready for playback
1340
1638
    SDL.audios.push({
1341
1639
      source: filename,
1342
 
      audio: raw
 
1640
      audio: audio
1343
1641
    });
1344
1642
    return id;
1345
1643
  },
1382
1680
    // If the user asks us to allocate a channel automatically, get the first
1383
1681
    // free one.
1384
1682
    if (channel == -1) {
1385
 
      channel = SDL.channelMinimumNumber;
1386
1683
      for (var i = SDL.channelMinimumNumber; i < SDL.numChannels; i++) {
1387
1684
        if (!SDL.channels[i].audio) {
1388
1685
          channel = i;
1389
1686
          break;
1390
1687
        }
1391
1688
      }
 
1689
      if (channel == -1) {
 
1690
        Module.printErr('All ' + SDL.numChannels + ' channels in use!');
 
1691
        return -1;
 
1692
      }
1392
1693
    }
1393
 
 
1394
1694
    // We clone the audio node to utilize the preloaded audio buffer, since
1395
1695
    // the browser has already preloaded the audio file.
1396
1696
    var channelInfo = SDL.channels[channel];
1397
1697
    channelInfo.audio = audio = audio.cloneNode(true);
1398
1698
    audio.numChannels = info.audio.numChannels;
1399
1699
    audio.frequency = info.audio.frequency;
1400
 
 
1401
1700
    // TODO: handle N loops. Behavior matches Mix_PlayMusic
1402
1701
    audio.loop = loops != 0; 
1403
 
    if (SDL.channelFinished) {
1404
 
      audio['onended'] = function() { // TODO: cache these
 
1702
    audio['onended'] = function() { // TODO: cache these
 
1703
      channelInfo.audio = null;
 
1704
      if (SDL.channelFinished) {
1405
1705
        Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
1406
1706
      }
1407
1707
    }
1458
1758
      audio.play();
1459
1759
    }
1460
1760
    audio.volume = channelInfo.volume;
1461
 
    audio.paused = false;
1462
1761
    return channel;
1463
1762
  },
1464
1763
  Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
1468
1767
  },
1469
1768
 
1470
1769
  Mix_HaltChannel: function(channel) {
1471
 
    var info = SDL.channels[channel];
1472
 
    if (info.audio) {
1473
 
      info.audio.pause();
1474
 
      info.audio = null;
 
1770
    function halt(channel) {
 
1771
      var info = SDL.channels[channel];
 
1772
      if (info.audio) {
 
1773
        info.audio.pause();
 
1774
        info.audio = null;
 
1775
      }
 
1776
      if (SDL.channelFinished) {
 
1777
        Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
 
1778
      }
1475
1779
    }
1476
 
    if (SDL.channelFinished) {
1477
 
      Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
 
1780
    if (channel != -1) {
 
1781
      halt(channel);
 
1782
    } else {
 
1783
      for (var i = 0; i < SDL.channels.length; ++i) halt(i);
1478
1784
    }
1479
1785
    return 0;
1480
1786
  },
1491
1797
    return SDL.setGetVolume(SDL.music, volume);
1492
1798
  },
1493
1799
 
1494
 
  Mix_LoadMUS: 'Mix_LoadWAV_RW',
1495
1800
  Mix_LoadMUS_RW: 'Mix_LoadWAV_RW',
 
1801
  Mix_LoadMUS__deps: ['Mix_LoadMUS_RW', 'SDL_RWFromFile', 'SDL_FreeRW'],
 
1802
  Mix_LoadMUS: function(filename) {
 
1803
    var rwops = _SDL_RWFromFile(filename);
 
1804
    var result = _Mix_LoadMUS_RW(rwops);
 
1805
    _SDL_FreeRW(rwops);
 
1806
    return result;
 
1807
  },
1496
1808
 
1497
1809
  Mix_FreeMusic: 'Mix_FreeChunk',
1498
1810
 
1509
1821
    }
1510
1822
    audio.volume = SDL.music.volume;
1511
1823
    audio['onended'] = _Mix_HaltMusic; // will send callback
 
1824
    if (SDL.music.audio) {
 
1825
      if (!SDL.music.audio.paused) {
 
1826
        Module.printErr('Music is already playing. ' + SDL.music.source);
 
1827
      }
 
1828
      SDL.music.audio.pause();
 
1829
    }
1512
1830
    SDL.music.audio = audio;
1513
1831
    return 0;
1514
1832
  },
1574
1892
    var info = SDL.channels[channel];
1575
1893
    if (info && info.audio) {
1576
1894
      info.audio.pause();
1577
 
      info.audio.paused = true;
 
1895
    } else {
 
1896
      Module.printErr('Mix_Pause: no sound found for channel: ' + channel);
1578
1897
    }
1579
1898
  },
1580
1899
  
1654
1973
  },
1655
1974
  TTF_RenderText_Blended: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
1656
1975
  TTF_RenderText_Shaded: 'TTF_RenderText_Solid', // XXX ignore blending vs. solid
 
1976
  TTF_RenderUTF8_Solid: 'TTF_RenderText_Solid',
1657
1977
 
1658
1978
  TTF_SizeText: function(font, text, w, h) {
1659
1979
    var fontData = SDL.fonts[font];
1676
1996
    return Math.floor(fontData.size*0.02); // XXX
1677
1997
  },
1678
1998
 
 
1999
  TTF_FontHeight: function(font) {
 
2000
    var fontData = SDL.fonts[font];
 
2001
    return fontData.size;
 
2002
  },
 
2003
 
1679
2004
  // SDL gfx
1680
2005
 
 
2006
  $SDL_gfx: {
 
2007
    drawRectangle: function(surf, x1, y1, x2, y2, action, cssColor) {
 
2008
      x1 = x1 << 16 >> 16;
 
2009
      y1 = y1 << 16 >> 16;
 
2010
      x2 = x2 << 16 >> 16;
 
2011
      y2 = y2 << 16 >> 16;
 
2012
      var surfData = SDL.surfaces[surf];
 
2013
      assert(!surfData.locked); // but we could unlock and re-lock if we must..
 
2014
      // TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
 
2015
      var x = x1 < x2 ? x1 : x2;
 
2016
      var y = y1 < y2 ? y1 : y2;
 
2017
      var w = Math.abs(x2 - x1);
 
2018
      var h = Math.abs(y2 - y1);
 
2019
      surfData.ctx.save();
 
2020
      surfData.ctx[action + 'Style'] = cssColor;
 
2021
      surfData.ctx[action + 'Rect'](x, y, w, h);
 
2022
      surfData.ctx.restore();
 
2023
    },
 
2024
    drawLine: function(surf, x1, y1, x2, y2, cssColor) {
 
2025
      x1 = x1 << 16 >> 16;
 
2026
      y1 = y1 << 16 >> 16;
 
2027
      x2 = x2 << 16 >> 16;
 
2028
      y2 = y2 << 16 >> 16;
 
2029
      var surfData = SDL.surfaces[surf];
 
2030
      assert(!surfData.locked); // but we could unlock and re-lock if we must..
 
2031
      surfData.ctx.save();
 
2032
      surfData.ctx.strokeStyle = cssColor;
 
2033
      surfData.ctx.beginPath();
 
2034
      surfData.ctx.moveTo(x1, y1);
 
2035
      surfData.ctx.lineTo(x2, y2);
 
2036
      surfData.ctx.stroke();
 
2037
      surfData.ctx.restore();
 
2038
    },
 
2039
    // See http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
 
2040
    drawEllipse: function(surf, x, y, rx, ry, action, cssColor) {
 
2041
      x = x << 16 >> 16;
 
2042
      y = y << 16 >> 16;
 
2043
      rx = rx << 16 >> 16;
 
2044
      ry = ry << 16 >> 16;
 
2045
      var surfData = SDL.surfaces[surf];
 
2046
      assert(!surfData.locked); // but we could unlock and re-lock if we must..
 
2047
 
 
2048
      surfData.ctx.save();
 
2049
      surfData.ctx.beginPath();
 
2050
      surfData.ctx.translate(x, y);
 
2051
      surfData.ctx.scale(rx, ry);
 
2052
      surfData.ctx.arc(0, 0, 1, 0, 2 * Math.PI);
 
2053
      surfData.ctx.restore();
 
2054
 
 
2055
      surfData.ctx.save();
 
2056
      surfData.ctx[action + 'Style'] = cssColor;
 
2057
      surfData.ctx[action]();
 
2058
      surfData.ctx.restore();
 
2059
    },
 
2060
    // the gfx library uses something different from the rest of SDL...
 
2061
    translateColorToCSSRGBA: function(rgba) {
 
2062
      return 'rgba(' + (rgba>>>24) + ',' + (rgba>>16 & 0xff) + ',' + (rgba>>8 & 0xff) + ',' + (rgba&0xff) + ')';
 
2063
    }
 
2064
  },
 
2065
 
 
2066
  boxColor__deps: ['$SDL_gfx'],
 
2067
  boxColor: function(surf, x1, y1, x2, y2, color) {
 
2068
    return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'fill', SDL_gfx.translateColorToCSSRGBA(color));
 
2069
  },
 
2070
 
 
2071
  boxRGBA__deps: ['$SDL_gfx'],
1681
2072
  boxRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
1682
 
    var surfData = SDL.surfaces[surf];
1683
 
    assert(!surfData.locked); // but we could unlock and re-lock if we must..
1684
 
    // TODO: if ctx does not change, leave as is, and also do not re-set xStyle etc.
1685
 
    surfData.ctx.save();
1686
 
    surfData.ctx.fillStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
1687
 
    surfData.ctx.fillRect(x1, y1, x2-x1, y2-y1);
1688
 
    surfData.ctx.restore();
1689
 
  },
1690
 
 
 
2073
    return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'fill', SDL.translateRGBAToCSSRGBA(r, g, b, a));
 
2074
  },
 
2075
 
 
2076
  rectangleColor__deps: ['$SDL_gfx'],
 
2077
  rectangleColor: function(surf, x1, y1, x2, y2, color) {
 
2078
    return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'stroke', SDL_gfx.translateColorToCSSRGBA(color));
 
2079
  },
 
2080
 
 
2081
  rectangleRGBA__deps: ['$SDL_gfx'],
1691
2082
  rectangleRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
1692
 
    var surfData = SDL.surfaces[surf];
1693
 
    assert(!surfData.locked); // but we could unlock and re-lock if we must..
1694
 
    surfData.ctx.save();
1695
 
    surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
1696
 
    surfData.ctx.strokeRect(x1, y1, x2-x1, y2-y1);
1697
 
    surfData.ctx.restore();
1698
 
  },
1699
 
 
 
2083
    return SDL_gfx.drawRectangle(surf, x1, y1, x2, y2, 'stroke', SDL.translateRGBAToCSSRGBA(r, g, b, a));
 
2084
  },
 
2085
 
 
2086
  ellipseColor__deps: ['$SDL_gfx'],
 
2087
  ellipseColor: function(surf, x, y, rx, ry, color) {
 
2088
    return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'stroke', SDL_gfx.translateColorToCSSRGBA(color));
 
2089
  },
 
2090
 
 
2091
  ellipseRGBA__deps: ['$SDL_gfx'],
 
2092
  ellipseRGBA: function(surf, x, y, rx, ry, r, g, b, a) {
 
2093
    return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'stroke', SDL.translateRGBAToCSSRGBA(r, g, b, a));
 
2094
  },
 
2095
 
 
2096
  filledEllipseColor__deps: ['$SDL_gfx'],
 
2097
  filledEllipseColor: function(surf, x, y, rx, ry, color) {
 
2098
    return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'fill', SDL_gfx.translateColorToCSSRGBA(color));
 
2099
  },
 
2100
 
 
2101
  filledEllipseRGBA__deps: ['$SDL_gfx'],
 
2102
  filledEllipseRGBA: function(surf, x, y, rx, ry, r, g, b, a) {
 
2103
    return SDL_gfx.drawEllipse(surf, x, y, rx, ry, 'fill', SDL.translateRGBAToCSSRGBA(r, g, b, a));
 
2104
  },
 
2105
 
 
2106
  lineColor__deps: ['$SDL_gfx'],
 
2107
  lineColor: function(surf, x1, y1, x2, y2, color) {
 
2108
    return SDL_gfx.drawLine(surf, x1, y1, x2, y2, SDL_gfx.translateColorToCSSRGBA(color));
 
2109
  },
 
2110
 
 
2111
  lineRGBA__deps: ['$SDL_gfx'],
1700
2112
  lineRGBA: function(surf, x1, y1, x2, y2, r, g, b, a) {
1701
 
    var surfData = SDL.surfaces[surf];
1702
 
    assert(!surfData.locked); // but we could unlock and re-lock if we must..
1703
 
    surfData.ctx.save();
1704
 
    surfData.ctx.strokeStyle = SDL.translateRGBAToCSSRGBA(r, g, b, a);
1705
 
    surfData.ctx.beginPath();
1706
 
    surfData.ctx.moveTo(x1, y1);
1707
 
    surfData.ctx.lineTo(x2, y2);
1708
 
    surfData.ctx.stroke();
1709
 
    surfData.ctx.restore();
 
2113
    return SDL_gfx.drawLine(surf, x1, y1, x2, y2, SDL.translateRGBAToCSSRGBA(r, g, b, a));
1710
2114
  },
1711
2115
 
1712
2116
  pixelRGBA__deps: ['boxRGBA'],
1721
2125
    console.log('TODO: SDL_GL_SetAttribute');
1722
2126
  },
1723
2127
 
1724
 
  SDL_GL_GetProcAddress__deps: ['$GLEmulation'],
 
2128
  SDL_GL_GetProcAddress__deps: ['emscripten_GetProcAddress'],
1725
2129
  SDL_GL_GetProcAddress: function(name_) {
1726
 
    return GLEmulation.getProcAddress(Pointer_stringify(name_));
 
2130
    return _emscripten_GetProcAddress(Pointer_stringify(name_));
1727
2131
  },
1728
2132
 
1729
2133
  SDL_GL_SwapBuffers: function() {},
1788
2192
    return -1;
1789
2193
  },
1790
2194
 
1791
 
  SDL_SetGammaRamp: function (redTable, greenTable, blueTable) {
 
2195
  SDL_SetGammaRamp: function(redTable, greenTable, blueTable) {
1792
2196
    return -1;
1793
2197
  },
1794
2198
 
 
2199
  // Joysticks
 
2200
 
 
2201
  SDL_NumJoysticks: function() { return 0; },
 
2202
 
 
2203
  SDL_JoystickName: function(deviceIndex) { return 0; },
 
2204
 
 
2205
  SDL_JoystickOpen: function(deviceIndex) { return 0; },
 
2206
 
 
2207
  SDL_JoystickOpened: function(deviceIndex) { return 0; },
 
2208
 
 
2209
  SDL_JoystickIndex: function(joystick) { return 0; },
 
2210
 
 
2211
  SDL_JoystickNumAxes: function(joystick) { return 0; },
 
2212
 
 
2213
  SDL_JoystickNumBalls: function(joystick) { return 0; },
 
2214
 
 
2215
  SDL_JoystickNumHats: function(joystick) { return 0; },
 
2216
 
 
2217
  SDL_JoystickNumButtons: function(joystick) { return 0; },
 
2218
 
 
2219
  SDL_JoystickUpdate: function() {},
 
2220
 
 
2221
  SDL_JoystickEventState: function(state) { return 0; },
 
2222
 
 
2223
  SDL_JoystickGetAxis: function(joystick, axis) { return 0; },
 
2224
 
 
2225
  SDL_JoystickGetHat: function(joystick, hat) { return 0; },
 
2226
 
 
2227
  SDL_JoystickGetBall: function(joystick, ball, dxptr, dyptr) { return -1; },
 
2228
 
 
2229
  SDL_JoystickGetButton: function(joystick, button) { return 0; },
 
2230
 
 
2231
  SDL_JoystickClose: function(joystick) {},
 
2232
 
1795
2233
  // Misc
1796
2234
 
1797
2235
  SDL_InitSubSystem: function(flags) { return 0 },
1798
2236
 
1799
 
  SDL_NumJoysticks: function() { return 0 },
 
2237
  SDL_RWFromConstMem: function(mem, size) {
 
2238
    var id = SDL.rwops.length; // TODO: recycle ids when they are null
 
2239
    SDL.rwops.push({ bytes: mem, count: size });
 
2240
    return id;
 
2241
  },
 
2242
  SDL_RWFromMem: 'SDL_RWFromConstMem',
1800
2243
 
1801
 
  SDL_RWFromFile: function(filename, mode) {
1802
 
    return filename; // XXX We just forward the filename
 
2244
  SDL_RWFromFile: function(_name, mode) {
 
2245
    var id = SDL.rwops.length; // TODO: recycle ids when they are null
 
2246
    var name = Pointer_stringify(_name)
 
2247
    SDL.rwops.push({ filename: name, mimetype: Browser.getMimetype(name) });
 
2248
    return id;
 
2249
  },
 
2250
  
 
2251
  SDL_FreeRW: function(rwopsID) {
 
2252
    SDL.rwops[rwopsID] = null;
 
2253
    while (SDL.rwops.length > 0 && SDL.rwops[SDL.rwops.length-1] === null) {
 
2254
      SDL.rwops.pop();
 
2255
    }
1803
2256
  },
1804
2257
 
1805
2258
  SDL_EnableUNICODE: function(on) {
1810
2263
 
1811
2264
  SDL_AddTimer: function(interval, callback, param) {
1812
2265
    return window.setTimeout(function() {
1813
 
      Runtime.dynCall('ii', callback, [interval, param]);
 
2266
      Runtime.dynCall('iii', callback, [interval, param]);
1814
2267
    }, interval);
1815
2268
  },
1816
2269
  SDL_RemoveTimer: function(id) {
1826
2279
  SDL_GetThreadID: function() { throw 'SDL_GetThreadID' },
1827
2280
  SDL_ThreadID: function() { throw 'SDL_ThreadID' },
1828
2281
  SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' },
1829
 
  SDL_FreeRW: function() { throw 'SDL_FreeRW: TODO' },
1830
2282
  SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' },
1831
2283
  SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' },
1832
2284
  SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' },
1833
2285
 
1834
 
  Mix_SetPostMix: function() { throw 'Mix_SetPostMix: TODO' },
 
2286
  Mix_SetPostMix: function() { Runtime.warnOnce('Mix_SetPostMix: TODO') },
1835
2287
  Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' },
1836
2288
  Mix_FadeInChannelTimed: function() { throw 'Mix_FadeInChannelTimed' },
1837
2289
  Mix_FadeOutChannel: function() { throw 'Mix_FadeOutChannel' },
1838
2290
 
1839
2291
  Mix_Linked_Version: function() { throw 'Mix_Linked_Version: TODO' },
1840
 
  SDL_CreateRGBSurfaceFrom: function() { throw 'SDL_CreateRGBSurfaceFrom: TODO' },
1841
2292
  SDL_SaveBMP_RW: function() { throw 'SDL_SaveBMP_RW: TODO' },
1842
2293
 
 
2294
  SDL_WM_SetIcon: function() { /* This function would set the application window icon surface, which doesn't apply for web canvases, so a no-op. */ },
1843
2295
  SDL_HasRDTSC: function() { return 0; },
1844
2296
  SDL_HasMMX: function() { return 0; },
1845
2297
  SDL_HasMMXExt: function() { return 0; },