Initial Emscripten merge.
authorRyan C. Gordon <icculus@icculus.org>
Sat, 13 Dec 2014 01:17:10 -0500
changeset 9277 743c0a496aa8
parent 9276 2b9bf6b04595
child 9278 e881c9e96650
Initial Emscripten merge.
CMakeLists.txt
build-scripts/config.sub
configure
configure.in
docs/README-emscripten.txt
include/SDL_atomic.h
include/SDL_config.h.cmake
include/SDL_config.h.in
include/SDL_hints.h
src/audio/SDL_audio.c
src/audio/emscripten/SDL_emscriptenaudio.c
src/audio/emscripten/SDL_emscriptenaudio.h
src/cpuinfo/SDL_cpuinfo.c
src/dynapi/SDL_dynapi.h
src/filesystem/emscripten/SDL_sysfilesystem.c
src/joystick/SDL_gamecontroller.c
src/joystick/SDL_gamecontrollerdb.h
src/joystick/emscripten/SDL_sysjoystick.c
src/joystick/emscripten/SDL_sysjoystick_c.h
src/power/SDL_power.c
src/power/emscripten/SDL_syspower.c
src/render/opengles2/SDL_gles2funcs.h
src/render/opengles2/SDL_render_gles2.c
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/emscripten/SDL_emscriptenevents.c
src/video/emscripten/SDL_emscriptenevents.h
src/video/emscripten/SDL_emscriptenframebuffer.c
src/video/emscripten/SDL_emscriptenframebuffer.h
src/video/emscripten/SDL_emscriptenmouse.c
src/video/emscripten/SDL_emscriptenmouse.h
src/video/emscripten/SDL_emscriptenopengles.c
src/video/emscripten/SDL_emscriptenopengles.h
src/video/emscripten/SDL_emscriptenvideo.c
src/video/emscripten/SDL_emscriptenvideo.h
test/Makefile.in
test/checkkeys.c
test/configure
test/configure.in
test/emscripten/joystick-pre.js
test/loopwave.c
test/testautomation_surface.c
test/testdraw2.c
test/testdrawchessboard.c
test/testfilesystem.c
test/testgamecontroller.c
test/testgesture.c
test/testgles2.c
test/testintersections.c
test/testjoystick.c
test/testmultiaudio.c
test/testoverlay2.c
test/testrelative.c
test/testrendercopyex.c
test/testrendertarget.c
test/testscale.c
test/testsprite2.c
test/testspriteminimal.c
test/teststreaming.c
test/testviewport.c
test/testwm2.c
--- a/CMakeLists.txt	Sat Dec 13 02:33:52 2014 -0500
+++ b/CMakeLists.txt	Sat Dec 13 01:17:10 2014 -0500
@@ -117,6 +117,12 @@
   set(UNIX_OR_MAC_SYS OFF)
 endif()
 
+if (UNIX_OR_MAC_SYS AND NOT EMSCRIPTEN) # JavaScript does not yet have threading support, so disable pthreads when building for Emscripten.
+  set(PTHREADS_ENABLED_BY_DEFAULT ON)
+else()
+  set(PTHREADS_ENABLED_BY_DEFAULT OFF)
+endif()
+
 # Default option knobs
 if(APPLE OR ARCH_64)
   set(OPT_DEF_SSEMATH ON)
@@ -170,13 +176,19 @@
 set(SDL_LIBS "-lSDL2")
 set(SDL_CFLAGS "")
 
+# Emscripten toolchain has a nonempty default value for this, and the checks 
+# in this file need to change that, so remember the original value, and 
+# restore back to that afterwards. For check_function_exists() to work in
+# Emscripten, this value must be at its default value.
+set(ORIG_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
+
 if(CYGWIN)
   # We build SDL on cygwin without the UNIX emulation layer
   include_directories("-I/usr/include/mingw")
   set(CMAKE_REQUIRED_FLAGS "-mno-cygwin")
   check_c_source_compiles("int main(int argc, char **argv) {}"
     HAVE_GCC_NO_CYGWIN)
-  set(CMAKE_REQUIRED_FLAGS)
+  set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
   if(HAVE_GCC_NO_CYGWIN)
     list(APPEND EXTRA_LDFLAGS "-mno-cygwin")
     list(APPEND SDL_LIBS "-mno-cygwin")
@@ -188,12 +200,35 @@
 # General includes
 include_directories(${SDL2_BINARY_DIR}/include ${SDL2_SOURCE_DIR}/include)
 
+if(EMSCRIPTEN)
+  # Set up default values for the currently supported set of subsystems:
+  # Emscripten/Javascript does not have assembly support, a dynamic library 
+  # loading architecture, low-level CPU inspection or multithreading.
+  set(OPT_DEF_ASM FALSE)
+  set(SDL_SHARED_ENABLED_BY_DEFAULT OFF)
+  set(SDL_ATOMIC_ENABLED_BY_DEFAULT OFF)
+  set(SDL_THREADS_ENABLED_BY_DEFAULT OFF)
+  set(SDL_LOADSO_ENABLED_BY_DEFAULT OFF)
+  set(SDL_CPUINFO_ENABLED_BY_DEFAULT OFF)
+  set(DLOPEN_ENABLED_BY_DEFAULT OFF)
+else()
+  set(SDL_SHARED_ENABLED_BY_DEFAULT ON)
+  set(SDL_ATOMIC_ENABLED_BY_DEFAULT ON)
+  set(SDL_THREADS_ENABLED_BY_DEFAULT ON)
+  set(SDL_LOADSO_ENABLED_BY_DEFAULT ON)
+  set(SDL_CPUINFO_ENABLED_BY_DEFAULT ON)
+  set(DLOPEN_ENABLED_BY_DEFAULT ON)
+endif()
+
 set(SDL_SUBSYSTEMS
     Atomic Audio Video Render Events Joystick Haptic Power Threads Timers
     File Loadso CPUinfo Filesystem)
 foreach(_SUB ${SDL_SUBSYSTEMS})
   string(TOUPPER ${_SUB} _OPT)
-  option(SDL_${_OPT} "Enable the ${_SUB} subsystem" ON)
+  if (NOT DEFINED SDL_${_OPT}_ENABLED_BY_DEFAULT)
+    set(SDL_${_OPT}_ENABLED_BY_DEFAULT ON)
+  endif()
+  option(SDL_${_OPT} "Enable the ${_SUB} subsystem" ${SDL_${_OPT}_ENABLED_BY_DEFAULT})
 endforeach()
 
 option_string(ASSERTIONS "Enable internal sanity checks (auto/disabled/release/enabled/paranoid)" "auto")
@@ -216,9 +251,9 @@
 set_option(VIDEO_DUMMY         "Use dummy video driver" ON)
 set_option(VIDEO_OPENGL        "Include OpenGL support" ON)
 set_option(VIDEO_OPENGLES      "Include OpenGL ES support" ON)
-set_option(PTHREADS            "Use POSIX threads for multi-threading" ${UNIX_OR_MAC_SYS})
+set_option(PTHREADS            "Use POSIX threads for multi-threading" ${PTHREADS_ENABLED_BY_DEFAULT})
 dep_option(PTHREADS_SEM        "Use pthread semaphores" ON "PTHREADS" OFF)
-set_option(SDL_DLOPEN          "Use dlopen for shared object loading" ON)
+set_option(SDL_DLOPEN          "Use dlopen for shared object loading" ${DLOPEN_ENABLED_BY_DEFAULT})
 set_option(OSS                 "Support the OSS audio API" ${UNIX_SYS})
 set_option(ALSA                "Support the ALSA audio API" ${UNIX_SYS})
 dep_option(ALSA_SHARED         "Dynamically load ALSA audio support" ON "ALSA" OFF)
@@ -251,7 +286,7 @@
 
 # TODO: We should (should we?) respect cmake's ${BUILD_SHARED_LIBS} flag here
 # The options below are for compatibility to configure's default behaviour.
-set(SDL_SHARED ON CACHE BOOL "Build a shared version of the library")
+set(SDL_SHARED ${SDL_SHARED_ENABLED_BY_DEFAULT} CACHE BOOL "Build a shared version of the library")
 set(SDL_STATIC ON CACHE BOOL "Build a static version of the library")
 
 # General source files
@@ -317,7 +352,7 @@
   set(CMAKE_REQUIRED_FLAGS "-mpreferred-stack-boundary=2")
   check_c_source_compiles("int x = 0; int main(int argc, char **argv) {}"
     HAVE_GCC_PREFERRED_STACK_BOUNDARY)
-  set(CMAKE_REQUIRED_FLAGS)
+  set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
 
   set(CMAKE_REQUIRED_FLAGS "-fvisibility=hidden -Werror")
   check_c_source_compiles("
@@ -328,7 +363,7 @@
   if(HAVE_GCC_FVISIBILITY)
     list(APPEND EXTRA_CFLAGS "-fvisibility=hidden")
   endif()
-  set(CMAKE_REQUIRED_FLAGS)
+  set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
 
   check_c_compiler_flag(-Wall HAVE_GCC_WALL)
   if(HAVE_GCC_WALL)
@@ -376,7 +411,7 @@
       if(HAVE_MMX)
         list(APPEND EXTRA_CFLAGS "-mmmx")
       endif()
-      set(CMAKE_REQUIRED_FLAGS)
+      set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
     endif()
 
     if(3DNOW)
@@ -393,7 +428,7 @@
       if(HAVE_3DNOW)
         list(APPEND EXTRA_CFLAGS "-m3dnow")
       endif()
-      set(CMAKE_REQUIRED_FLAGS)
+      set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
     endif()
 
     if(SSE)
@@ -416,7 +451,7 @@
       if(HAVE_SSE)
         list(APPEND EXTRA_CFLAGS "-msse")
       endif()
-      set(CMAKE_REQUIRED_FLAGS)
+      set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
     endif()
 
     if(SSE2)
@@ -439,7 +474,7 @@
       if(HAVE_SSE2)
         list(APPEND EXTRA_CFLAGS "-msse2")
       endif()
-      set(CMAKE_REQUIRED_FLAGS)
+      set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
     endif()
 
     if(SSEMATH)
@@ -464,7 +499,7 @@
               return vec_splat_u32(0);
           }
           int main(int argc, char **argv) { }" HAVE_ALTIVEC)
-      set(CMAKE_REQUIRED_FLAGS)
+      set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
       if(HAVE_ALTIVEC OR HAVE_ALTIVEC_H_HDR)
         set(HAVE_ALTIVEC TRUE) # if only HAVE_ALTIVEC_H_HDR is set
         list(APPEND EXTRA_CFLAGS "-maltivec")
@@ -642,7 +677,49 @@
 endif()
 
 # Platform-specific options and settings
-if(UNIX AND NOT APPLE)
+if(EMSCRIPTEN)
+  # Hide noisy warnings that intend to aid mostly during initial stages of porting a new
+  # project. Uncomment at will for verbose cross-compiling -I/../ path info.
+  add_definitions(-Wno-warn-absolute-paths)
+  if(SDL_AUDIO)
+    set(SDL_AUDIO_DRIVER_EMSCRIPTEN 1)
+    file(GLOB EM_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/emscripten/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${EM_AUDIO_SOURCES})
+    set(HAVE_SDL_AUDIO TRUE)
+  endif()
+  if(SDL_FILESYSTEM)
+    set(SDL_FILESYSTEM_EMSCRIPTEN 1)
+    file(GLOB EM_FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/emscripten/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${EM_FILESYSTEM_SOURCES})
+    set(HAVE_SDL_FILESYSTEM TRUE)
+  endif()
+  if(SDL_JOYSTICK)
+    set(SDL_JOYSTICK_EMSCRIPTEN 1)
+    file(GLOB EM_JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/emscripten/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${EM_JOYSTICK_SOURCES})
+    set(HAVE_SDL_JOYSTICK TRUE)
+  endif()
+  if(SDL_POWER)
+    set(SDL_POWER_EMSCRIPTEN 1)
+    file(GLOB EM_POWER_SOURCES ${SDL2_SOURCE_DIR}/src/power/emscripten/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${EM_POWER_SOURCES})
+    set(HAVE_SDL_POWER TRUE)
+  endif()
+  if(SDL_VIDEO)
+    set(SDL_VIDEO_DRIVER_EMSCRIPTEN 1)
+    file(GLOB EM_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/emscripten/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${EM_VIDEO_SOURCES})
+    set(HAVE_SDL_VIDEO TRUE)
+
+    #enable gles
+    if(VIDEO_OPENGLES)
+      set(SDL_VIDEO_OPENGL_EGL 1)
+      set(HAVE_VIDEO_OPENGLES TRUE)
+      set(SDL_VIDEO_OPENGL_ES2 1)
+      set(SDL_VIDEO_RENDER_OGL_ES2 1)
+    endif()
+  endif()
+elseif(UNIX AND NOT APPLE)
   if(SDL_AUDIO)
     if(SYSV5 OR SOLARIS OR HPUX)
         set(SDL_AUDIO_DRIVER_SUNAUDIO 1)
@@ -829,7 +906,7 @@
       link_directories($ENV{DXSDK_DIR}\\lib\\${PROCESSOR_ARCH})
       include_directories($ENV{DXSDK_DIR}\\Include)
     endif()
-    set(CMAKE_REQUIRED_FLAGS)
+    set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
   endif()
 
   if(SDL_AUDIO)
--- a/build-scripts/config.sub	Sat Dec 13 02:33:52 2014 -0500
+++ b/build-scripts/config.sub	Sat Dec 13 01:17:10 2014 -0500
@@ -1534,6 +1534,8 @@
 	-pnacl*)
 		os=-pnacl
 		;;
+	-emscripten*)
+		;;
 	-none)
 		;;
 	*)
--- a/configure	Sat Dec 13 02:33:52 2014 -0500
+++ b/configure	Sat Dec 13 01:17:10 2014 -0500
@@ -21385,6 +21385,78 @@
     fi
 }
 
+CheckEmscriptenGLES()
+{
+    if test x$enable_video = xyes -a x$enable_video_opengles = xyes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EGL support" >&5
+$as_echo_n "checking for EGL support... " >&6; }
+        video_opengl_egl=no
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+         #include <EGL/egl.h>
+
+int
+main ()
+{
+
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+        video_opengl_egl=yes
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $video_opengl_egl" >&5
+$as_echo "$video_opengl_egl" >&6; }
+        if test x$video_opengl_egl = xyes; then
+
+$as_echo "#define SDL_VIDEO_OPENGL_EGL 1" >>confdefs.h
+
+        fi
+
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenGL ES v2 headers" >&5
+$as_echo_n "checking for OpenGL ES v2 headers... " >&6; }
+        video_opengles_v2=no
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+         #include <GLES2/gl2.h>
+         #include <GLES2/gl2ext.h>
+
+int
+main ()
+{
+
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+        video_opengles_v2=yes
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $video_opengles_v2" >&5
+$as_echo "$video_opengles_v2" >&6; }
+        if test x$video_opengles_v2 = xyes; then
+
+$as_echo "#define SDL_VIDEO_OPENGL_ES2 1" >>confdefs.h
+
+
+$as_echo "#define SDL_VIDEO_RENDER_OGL_ES2 1" >>confdefs.h
+
+            SUMMARY_video="${SUMMARY_video} opengl_es2"
+        fi
+    fi
+}
+
 CheckInputEvents()
 {
             { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux 2.4 unified input interface" >&5
@@ -23483,7 +23555,68 @@
             SOURCES="$SOURCES $srcdir/src/filesystem/nacl/*.c"
             have_filesystem=yes
         fi
-
+        ;;
+    *-*-emscripten* )
+        if test x$enable_video = xyes; then
+
+$as_echo "#define SDL_VIDEO_DRIVER_EMSCRIPTEN 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/video/emscripten/*.c"
+            have_video=yes
+            SUMMARY_video="${SUMMARY_video} emscripten"
+        fi
+
+        if test x$enable_audio = xyes; then
+
+$as_echo "#define SDL_AUDIO_DRIVER_EMSCRIPTEN 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/audio/emscripten/*.c"
+            have_audio=yes
+            SUMMARY_audio="${SUMMARY_audio} emscripten"
+        fi
+
+        CheckVisibilityHidden
+        CheckDummyVideo
+        CheckDiskAudio
+        CheckDummyAudio
+        CheckDLOPEN
+        CheckClockGettime
+        CheckEmscriptenGLES
+
+         # Set up files for the power library
+        if test x$enable_power = xyes; then
+
+$as_echo "#define SDL_POWER_EMSCRIPTEN 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/power/emscripten/*.c"
+            have_power=yes
+        fi
+
+        # Set up files for the power library
+        if test x$enable_joystick = xyes; then
+
+$as_echo "#define SDL_JOYSTICK_EMSCRIPTEN 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/joystick/emscripten/*.c"
+            have_joystick=yes
+        fi
+
+        # Set up files for the filesystem library
+        if test x$enable_filesystem = xyes; then
+
+$as_echo "#define SDL_FILESYSTEM_EMSCRIPTEN 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/filesystem/emscripten/*.c"
+            have_filesystem=yes
+        fi
+        # Set up files for the timer library
+        if test x$enable_timers = xyes; then
+
+$as_echo "#define SDL_TIMER_UNIX 1" >>confdefs.h
+
+            SOURCES="$SOURCES $srcdir/src/timer/unix/*.c"
+            have_timers=yes
+        fi
         ;;
     *)
         as_fn_error $? "
--- a/configure.in	Sat Dec 13 02:33:52 2014 -0500
+++ b/configure.in	Sat Dec 13 01:17:10 2014 -0500
@@ -2130,6 +2130,40 @@
     fi
 }
 
+CheckEmscriptenGLES()
+{
+    if test x$enable_video = xyes -a x$enable_video_opengles = xyes; then
+        AC_MSG_CHECKING(for EGL support)
+        video_opengl_egl=no
+        AC_TRY_COMPILE([
+         #include <EGL/egl.h>
+        ],[
+        ],[
+        video_opengl_egl=yes
+        ])
+        AC_MSG_RESULT($video_opengl_egl)
+        if test x$video_opengl_egl = xyes; then
+            AC_DEFINE(SDL_VIDEO_OPENGL_EGL, 1, [ ])
+        fi
+
+        AC_MSG_CHECKING(for OpenGL ES v2 headers)
+        video_opengles_v2=no
+        AC_TRY_COMPILE([
+         #include <GLES2/gl2.h>
+         #include <GLES2/gl2ext.h>
+        ],[
+        ],[
+        video_opengles_v2=yes
+        ])
+        AC_MSG_RESULT($video_opengles_v2)
+        if test x$video_opengles_v2 = xyes; then
+            AC_DEFINE(SDL_VIDEO_OPENGL_ES2, 1, [ ])
+            AC_DEFINE(SDL_VIDEO_RENDER_OGL_ES2, 1, [ ])
+            SUMMARY_video="${SUMMARY_video} opengl_es2"
+        fi
+    fi
+}
+
 dnl See if we can use the new unified event interface in Linux 2.4
 CheckInputEvents()
 {
@@ -3302,7 +3336,56 @@
             SOURCES="$SOURCES $srcdir/src/filesystem/nacl/*.c"
             have_filesystem=yes
         fi
+        ;;
+    *-*-emscripten* )
+        if test x$enable_video = xyes; then
+            AC_DEFINE(SDL_VIDEO_DRIVER_EMSCRIPTEN, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/video/emscripten/*.c"
+            have_video=yes
+            SUMMARY_video="${SUMMARY_video} emscripten"
+        fi
+
+        if test x$enable_audio = xyes; then
+            AC_DEFINE(SDL_AUDIO_DRIVER_EMSCRIPTEN, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/audio/emscripten/*.c"
+            have_audio=yes
+            SUMMARY_audio="${SUMMARY_audio} emscripten"
+        fi
+
+        CheckVisibilityHidden
+        CheckDummyVideo
+        CheckDiskAudio
+        CheckDummyAudio
+        CheckDLOPEN
+        CheckClockGettime
+        CheckEmscriptenGLES
+
+         # Set up files for the power library
+        if test x$enable_power = xyes; then
+            AC_DEFINE(SDL_POWER_EMSCRIPTEN, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/power/emscripten/*.c"
+            have_power=yes
+        fi
         
+        # Set up files for the power library
+        if test x$enable_joystick = xyes; then
+            AC_DEFINE(SDL_JOYSTICK_EMSCRIPTEN, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/joystick/emscripten/*.c"
+            have_joystick=yes
+        fi
+
+        # Set up files for the filesystem library
+        if test x$enable_filesystem = xyes; then
+            AC_DEFINE(SDL_FILESYSTEM_EMSCRIPTEN, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/filesystem/emscripten/*.c"
+            have_filesystem=yes
+        fi
+        # Set up files for the timer library
+        if test x$enable_timers = xyes; then
+            AC_DEFINE(SDL_TIMER_UNIX, 1, [ ])
+            SOURCES="$SOURCES $srcdir/src/timer/unix/*.c"
+            have_timers=yes
+        fi
         ;;
     *)
         AC_MSG_ERROR([
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/README-emscripten.txt	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,26 @@
+Build:
+$ emconfigure ./configure --host=asmjs-unknown-emscripten --disable-assembly --disable-threads --enable-cpuinfo=false CFLAGS="-O2"
+$ emmake make
+
+Or with cmake:
+$ emconfigure cmake ..
+$ make
+
+To build one of the tests -
+$ cd test/
+$ emcc -O2 --js-opts 0 -g4 testdraw2.c -I../include ../build/.libs/libSDL2.a ../build/libSDL2_test.a -o a.html
+
+Uses GLES2 renderer or software
+
+tests: https://dl.dropboxusercontent.com/u/17360362/SDL2-em/index.html
+
+Some other SDL2 libraries can be easily built (assuming SDL2 is installed somwhere):
+
+SDL_mixer (http://www.libsdl.org/projects/SDL_mixer/)
+    $ EMCONFIGURE_JS=1 emconfigure ../configure
+    build as usual...
+
+SDL_gfx (http://cms.ferzkopp.net/index.php/software/13-sdl-gfx):
+    $ EMCONFIGURE_JS=1 emconfigure ../configure --disable-mmx
+    build as usual...
+
--- a/include/SDL_atomic.h	Sat Dec 13 02:33:52 2014 -0500
+++ b/include/SDL_atomic.h	Sat Dec 13 01:17:10 2014 -0500
@@ -122,7 +122,7 @@
 void _ReadWriteBarrier(void);
 #pragma intrinsic(_ReadWriteBarrier)
 #define SDL_CompilerBarrier()   _ReadWriteBarrier()
-#elif defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
+#elif (defined(__GNUC__) && !defined(__EMSCRIPTEN__)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
 /* This is correct for all CPUs when using GCC or Solaris Studio 12.1+. */
 #define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
 #else
--- a/include/SDL_config.h.cmake	Sat Dec 13 02:33:52 2014 -0500
+++ b/include/SDL_config.h.cmake	Sat Dec 13 01:17:10 2014 -0500
@@ -217,6 +217,7 @@
 #cmakedefine SDL_AUDIO_DRIVER_WINMM @SDL_AUDIO_DRIVER_WINMM@
 #cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND @SDL_AUDIO_DRIVER_FUSIONSOUND@
 #cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC @SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC@
+#cmakedefine SDL_AUDIO_DRIVER_EMSCRIPTEN @SDL_AUDIO_DRIVER_EMSCRIPTEN@
 
 /* Enable various input drivers */
 #cmakedefine SDL_INPUT_LINUXEV @SDL_INPUT_LINUXEV@
@@ -230,6 +231,7 @@
 #cmakedefine SDL_JOYSTICK_WINMM @SDL_JOYSTICK_WINMM@
 #cmakedefine SDL_JOYSTICK_USBHID @SDL_JOYSTICK_USBHID@
 #cmakedefine SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H @SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H@
+#cmakedefine SDL_JOYSTICK_EMSCRIPTEN @SDL_JOYSTICK_EMSCRIPTEN@
 #cmakedefine SDL_HAPTIC_DUMMY @SDL_HAPTIC_DUMMY@
 #cmakedefine SDL_HAPTIC_LINUX @SDL_HAPTIC_LINUX@
 #cmakedefine SDL_HAPTIC_IOKIT @SDL_HAPTIC_IOKIT@
@@ -279,6 +281,7 @@
 #cmakedefine SDL_VIDEO_DRIVER_MIR @SDL_VIDEO_DRIVER_MIR@
 #cmakedefine SDL_VIDEO_DRIVER_MIR_DYNAMIC @SDL_VIDEO_DRIVER_MIR_DYNAMIC@
 #cmakedefine SDL_VIDEO_DRIVER_MIR_DYNAMIC_XKBCOMMON @SDL_VIDEO_DRIVER_MIR_DYNAMIC_XKBCOMMON@
+#cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN @SDL_VIDEO_DRIVER_EMSCRIPTEN@
 #cmakedefine SDL_VIDEO_DRIVER_X11 @SDL_VIDEO_DRIVER_X11@
 #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC @SDL_VIDEO_DRIVER_X11_DYNAMIC@
 #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT @SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT@
@@ -325,6 +328,7 @@
 #cmakedefine SDL_POWER_WINDOWS @SDL_POWER_WINDOWS@
 #cmakedefine SDL_POWER_MACOSX @SDL_POWER_MACOSX@
 #cmakedefine SDL_POWER_HAIKU @SDL_POWER_HAIKU@
+#cmakedefine SDL_POWER_EMSCRIPTEN @SDL_POWER_EMSCRIPTEN@
 #cmakedefine SDL_POWER_HARDWIRED @SDL_POWER_HARDWIRED@
 
 /* Enable system filesystem support */
@@ -333,6 +337,7 @@
 #cmakedefine SDL_FILESYSTEM_DUMMY @SDL_FILESYSTEM_DUMMY@
 #cmakedefine SDL_FILESYSTEM_UNIX @SDL_FILESYSTEM_UNIX@
 #cmakedefine SDL_FILESYSTEM_WINDOWS @SDL_FILESYSTEM_WINDOWS@
+#cmakedefine SDL_FILESYSTEM_EMSCRIPTEN @SDL_FILESYSTEM_EMSCRIPTEN@
 
 /* Enable assembly routines */
 #cmakedefine SDL_ASSEMBLY_ROUTINES @SDL_ASSEMBLY_ROUTINES@
--- a/include/SDL_config.h.in	Sat Dec 13 02:33:52 2014 -0500
+++ b/include/SDL_config.h.in	Sat Dec 13 01:17:10 2014 -0500
@@ -228,6 +228,7 @@
 #undef SDL_AUDIO_DRIVER_WINMM
 #undef SDL_AUDIO_DRIVER_FUSIONSOUND
 #undef SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC
+#undef SDL_AUDIO_DRIVER_EMSCRIPTEN
 
 /* Enable various input drivers */
 #undef SDL_INPUT_LINUXEV
@@ -243,6 +244,7 @@
 #undef SDL_JOYSTICK_WINMM
 #undef SDL_JOYSTICK_USBHID
 #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
+#undef SDL_JOYSTICK_EMSCRIPTEN
 #undef SDL_HAPTIC_DUMMY
 #undef SDL_HAPTIC_LINUX
 #undef SDL_HAPTIC_IOKIT
@@ -287,6 +289,7 @@
 #undef SDL_VIDEO_DRIVER_X11
 #undef SDL_VIDEO_DRIVER_RPI
 #undef SDL_VIDEO_DRIVER_ANDROID
+#undef SDL_VIDEO_DRIVER_EMSCRIPTEN
 #undef SDL_VIDEO_DRIVER_X11_DYNAMIC
 #undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT
 #undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR
@@ -336,6 +339,7 @@
 #undef SDL_POWER_MACOSX
 #undef SDL_POWER_HAIKU
 #undef SDL_POWER_ANDROID
+#undef SDL_POWER_EMSCRIPTEN
 #undef SDL_POWER_HARDWIRED
 
 /* Enable system filesystem support */
@@ -345,6 +349,7 @@
 #undef SDL_FILESYSTEM_UNIX
 #undef SDL_FILESYSTEM_WINDOWS
 #undef SDL_FILESYSTEM_NACL
+#undef SDL_FILESYSTEM_EMSCRIPTEN
 
 /* Enable assembly routines */
 #undef SDL_ASSEMBLY_ROUTINES
--- a/include/SDL_hints.h	Sat Dec 13 02:33:52 2014 -0500
+++ b/include/SDL_hints.h	Sat Dec 13 01:17:10 2014 -0500
@@ -533,6 +533,20 @@
 #define SDL_HINT_IME_INTERNAL_EDITING "SDL_IME_INTERNAL_EDITING"
 
 /**
+ *  \brief override the binding element for keyboard inputs for Emscripten builds
+ *
+ * This hint only applies to the emscripten platform
+ *
+ * The variable can be one of
+ *    "#window"      - The javascript window object (this is the default)
+ *    "#document"    - The javascript document object
+ *    "#screen"      - the javascript window.screen object
+ *    "#canvas"      - the WebGL canvas element
+ *    any other string without a leading # sign apples to the element on the page with that ID.
+ */
+#define SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT   "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT"
+
+/**
  *  \brief  An enumeration of hint priorities
  */
 typedef enum
--- a/src/audio/SDL_audio.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/audio/SDL_audio.c	Sat Dec 13 01:17:10 2014 -0500
@@ -71,6 +71,7 @@
 extern AudioBootStrap ANDROIDAUD_bootstrap;
 extern AudioBootStrap PSPAUD_bootstrap;
 extern AudioBootStrap SNDIO_bootstrap;
+extern AudioBootStrap EmscriptenAudio_bootstrap;
 
 
 /* Available audio drivers */
@@ -141,6 +142,9 @@
 #if SDL_AUDIO_DRIVER_PSP
     &PSPAUD_bootstrap,
 #endif
+#if SDL_AUDIO_DRIVER_EMSCRIPTEN
+    &EmscriptenAudio_bootstrap,
+#endif
     NULL
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audio/emscripten/SDL_emscriptenaudio.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,271 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_AUDIO_DRIVER_EMSCRIPTEN
+
+#include "SDL_audio.h"
+#include "SDL_log.h"
+#include "../SDL_audio_c.h"
+#include "SDL_emscriptenaudio.h"
+
+#include <emscripten/emscripten.h>
+
+static int
+copyData(_THIS)
+{
+    int byte_len;
+
+    if (this->hidden->write_off + this->convert.len_cvt > this->hidden->mixlen) {
+        if (this->hidden->write_off > this->hidden->read_off) {
+            SDL_memmove(this->hidden->mixbuf,
+                        this->hidden->mixbuf + this->hidden->read_off,
+                        this->hidden->mixlen - this->hidden->read_off);
+            this->hidden->write_off = this->hidden->write_off - this->hidden->read_off;
+        } else {
+            this->hidden->write_off = 0;
+        }
+        this->hidden->read_off = 0;
+    }
+
+    SDL_memcpy(this->hidden->mixbuf + this->hidden->write_off,
+               this->convert.buf,
+               this->convert.len_cvt);
+    this->hidden->write_off += this->convert.len_cvt;
+    byte_len = this->hidden->write_off - this->hidden->read_off;
+
+    return byte_len;
+}
+
+static void
+HandleAudioProcess(_THIS)
+{
+    Uint8 *buf = NULL;
+    int byte_len = 0;
+    int bytes = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
+    int bytes_in = SDL_AUDIO_BITSIZE(this->convert.src_format) / 8;
+    int i;
+
+    /* Only do soemthing if audio is enabled */
+    if (!this->enabled)
+        return;
+
+    if (this->paused)
+        return;
+
+    if (this->convert.needed) {
+        if (this->hidden->conv_in_len != 0) {
+            this->convert.len = this->hidden->conv_in_len * bytes_in * this->spec.channels;
+        }
+
+        (*this->spec.callback) (this->spec.userdata,
+                                 this->convert.buf,
+                                 this->convert.len);
+        SDL_ConvertAudio(&this->convert);
+        buf = this->convert.buf;
+        byte_len = this->convert.len_cvt;
+
+        /* size mismatch*/
+        if (byte_len != this->spec.size) {
+            if (!this->hidden->mixbuf) {
+                this->hidden->mixlen = this->spec.size > byte_len ? this->spec.size * 2 : byte_len * 2;
+                this->hidden->mixbuf = SDL_malloc(this->hidden->mixlen);
+            }
+
+            /* copy existing data */
+            byte_len = copyData(this);
+
+            /* read more data*/
+            while (byte_len < this->spec.size) {
+                (*this->spec.callback) (this->spec.userdata,
+                                         this->convert.buf,
+                                         this->convert.len);
+                SDL_ConvertAudio(&this->convert);
+                byte_len = copyData(this);
+            }
+
+            byte_len = this->spec.size;
+            buf = this->hidden->mixbuf + this->hidden->read_off;
+            this->hidden->read_off += byte_len;
+        }
+
+    } else {
+        if (!this->hidden->mixbuf) {
+            this->hidden->mixlen = this->spec.size;
+            this->hidden->mixbuf = SDL_malloc(this->hidden->mixlen);
+        }
+        (*this->spec.callback) (this->spec.userdata,
+                                 this->hidden->mixbuf,
+                                 this->hidden->mixlen);
+        buf = this->hidden->mixbuf;
+        byte_len = this->hidden->mixlen;
+    }
+
+    if (buf) {
+        EM_ASM_ARGS({
+            var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels'];
+            for (var c = 0; c < numChannels; ++c) {
+                var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c);
+                if (channelData.length != $1) {
+                    throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
+                }
+
+                for (var j = 0; j < $1; ++j) {
+                    channelData[j] = getValue($0 + (j*numChannels + c)*4, 'float');
+                }
+            }
+        }, buf, byte_len / bytes / this->spec.channels);
+    }
+}
+
+static void
+Emscripten_CloseDevice(_THIS)
+{
+    if (this->hidden != NULL) {
+        if (this->hidden->mixbuf != NULL) {
+            /* Clean up the audio buffer */
+            SDL_free(this->hidden->mixbuf);
+            this->hidden->mixbuf = NULL;
+        }
+
+        SDL_free(this->hidden);
+        this->hidden = NULL;
+    }
+}
+
+static int
+Emscripten_OpenDevice(_THIS, const char *devname, int iscapture)
+{
+    SDL_bool valid_format = SDL_FALSE;
+    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
+    int i;
+    float f;
+
+    while ((!valid_format) && (test_format)) {
+        switch (test_format) {
+        case AUDIO_F32: /* web audio only supports floats */
+            this->spec.format = test_format;
+
+            valid_format = SDL_TRUE;
+            break;
+        }
+        test_format = SDL_NextAudioFormat();
+    }
+
+    if (!valid_format) {
+        /* Didn't find a compatible format :( */
+        return SDL_SetError("No compatible audio format!");
+    }
+
+    /* Initialize all variables that we clean on shutdown */
+    this->hidden = (struct SDL_PrivateAudioData *)
+        SDL_malloc((sizeof *this->hidden));
+    if (this->hidden == NULL) {
+        return SDL_OutOfMemory();
+    }
+    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+
+    /* based on parts of library_sdl.js */
+
+    /* create context (TODO: this puts stuff in the global namespace...)*/
+    EM_ASM({
+        if(typeof(SDL2) === 'undefined')
+            SDL2 = {};
+
+        if(typeof(SDL2.audio) === 'undefined')
+            SDL2.audio = {};
+
+        if (!SDL2.audioContext) {
+            if (typeof(AudioContext) !== 'undefined') {
+                SDL2.audioContext = new AudioContext();
+            } else if (typeof(webkitAudioContext) !== 'undefined') {
+                SDL2.audioContext = new webkitAudioContext();
+            } else {
+                throw 'Web Audio API is not available!';
+            }
+        }
+    });
+
+    /* limit to native freq */
+    int sampleRate = EM_ASM_INT_V({
+        return SDL2.audioContext['sampleRate'];
+    });
+
+    if(this->spec.freq != sampleRate) {
+        for (i = this->spec.samples; i > 0; i--) {
+            f = (float)i / (float)sampleRate * (float)this->spec.freq;
+            if (SDL_floor(f) == f) {
+                this->hidden->conv_in_len = SDL_floor(f);
+                break;
+            }
+        }
+
+        this->spec.freq = sampleRate;
+    }
+
+    SDL_CalculateAudioSpec(&this->spec);
+
+    /* setup a ScriptProcessorNode */
+    EM_ASM_ARGS({
+        SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
+        SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
+            SDL2.audio.currentOutputBuffer = e['outputBuffer'];
+            Runtime.dynCall('vi', $2, [$3]);
+        };
+        SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
+    }, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
+    return 0;
+}
+
+static int
+Emscripten_Init(SDL_AudioDriverImpl * impl)
+{
+    /* Set the function pointers */
+    impl->OpenDevice = Emscripten_OpenDevice;
+    impl->CloseDevice = Emscripten_CloseDevice;
+
+    /* only one output */
+    impl->OnlyHasDefaultOutputDevice = 1;
+
+    /* no threads here */
+    impl->SkipMixerLock = 1;
+    impl->ProvidesOwnCallbackThread = 1;
+
+    /* check availability */
+    int available = EM_ASM_INT_V({
+        if (typeof(AudioContext) !== 'undefined') {
+            return 1;
+        } else if (typeof(webkitAudioContext) !== 'undefined') {
+            return 1;
+        }
+        return 0;
+    });
+
+    return available;
+}
+
+AudioBootStrap EmscriptenAudio_bootstrap = {
+    "emscripten", "SDL emscripten audio driver", Emscripten_Init, 0
+};
+
+#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audio/emscripten/SDL_emscriptenaudio.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,42 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_emscriptenaudio_h
+#define _SDL_emscriptenaudio_h
+
+#include "../SDL_sysaudio.h"
+
+/* Hidden "this" pointer for the audio functions */
+#define _THIS   SDL_AudioDevice *this
+
+struct SDL_PrivateAudioData
+{
+    Uint8 *mixbuf;
+    Uint32 mixlen;
+
+    Uint32 conv_in_len;
+
+    Uint32 write_off, read_off;
+};
+
+#endif /* _SDL_emscriptenaudio_h */
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/cpuinfo/SDL_cpuinfo.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/cpuinfo/SDL_cpuinfo.c	Sat Dec 13 01:17:10 2014 -0500
@@ -79,6 +79,7 @@
 {
     int has_CPUID = 0;
 /* *INDENT-OFF* */
+#ifndef SDL_CPUINFO_DISABLED
 #if defined(__GNUC__) && defined(i386)
     __asm__ (
 "        pushfl                      # Get original EFLAGS             \n"
@@ -165,6 +166,7 @@
 "1:                            \n"
     );
 #endif
+#endif
 /* *INDENT-ON* */
     return has_CPUID;
 }
@@ -272,6 +274,7 @@
 CPU_haveAltiVec(void)
 {
     volatile int altivec = 0;
+#ifndef SDL_CPUINFO_DISABLED
 #if (defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__))) || (defined(__OpenBSD__) && defined(__powerpc__))
 #ifdef __OpenBSD__
     int selectors[2] = { CTL_MACHDEP, CPU_ALTIVEC };
@@ -292,6 +295,7 @@
     }
     signal(SIGILL, handler);
 #endif
+#endif
     return altivec;
 }
 
@@ -418,6 +422,7 @@
 SDL_GetCPUCount(void)
 {
     if (!SDL_CPUCount) {
+#ifndef SDL_CPUINFO_DISABLED
 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
         if (SDL_CPUCount <= 0) {
             SDL_CPUCount = (int)sysconf(_SC_NPROCESSORS_ONLN);
@@ -436,6 +441,7 @@
             SDL_CPUCount = info.dwNumberOfProcessors;
         }
 #endif
+#endif
         /* There has to be at least 1, right? :) */
         if (SDL_CPUCount <= 0) {
             SDL_CPUCount = 1;
@@ -723,6 +729,7 @@
 SDL_GetSystemRAM(void)
 {
     if (!SDL_SystemRAM) {
+#ifndef SDL_CPUINFO_DISABLED
 #if defined(HAVE_SYSCONF) && defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
         if (SDL_SystemRAM <= 0) {
             SDL_SystemRAM = (int)((Sint64)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) / (1024*1024));
@@ -757,6 +764,7 @@
             }
         }
 #endif
+#endif
     }
     return SDL_SystemRAM;
 }
--- a/src/dynapi/SDL_dynapi.h	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/dynapi/SDL_dynapi.h	Sat Dec 13 01:17:10 2014 -0500
@@ -43,7 +43,7 @@
 #include "TargetConditionals.h"
 #endif
 
-#if TARGET_OS_IPHONE || __native_client__  /* probably not useful on iOS or NACL. */
+#if TARGET_OS_IPHONE || __native_client__ || __EMSCRIPTEN__  /* probably not useful on iOS, NACL or Emscripten. */
 #define SDL_DYNAMIC_API 0
 #elif SDL_BUILDING_WINRT /* probaly not useful on WinRT, given current .dll loading restrictions */
 #define SDL_DYNAMIC_API 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/filesystem/emscripten/SDL_sysfilesystem.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,68 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_FILESYSTEM_EMSCRIPTEN
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* System dependent filesystem routines                                */
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "SDL_error.h"
+#include "SDL_filesystem.h"
+
+#include <emscripten/emscripten.h>
+
+char *
+SDL_GetBasePath(void)
+{
+    char *retval = "/";
+    return SDL_strdup(retval);
+}
+
+char *
+SDL_GetPrefPath(const char *org, const char *app)
+{
+    const char *append = "/libsdl/";
+    char *retval;
+    size_t len = 0;
+
+    len = SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
+    retval = (char *) SDL_malloc(len);
+    if (!retval) {
+        SDL_OutOfMemory();
+        return NULL;
+    }
+
+    SDL_snprintf(retval, len, "%s%s/%s/", append, org, app);
+
+    if (mkdir(retval, 0700) != 0 && errno != EEXIST) {
+        SDL_SetError("Couldn't create directory '%s': '%s'", retval, strerror(errno));
+        return NULL;
+    }
+
+    return retval;
+}
+
+#endif /* SDL_FILESYSTEM_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/joystick/SDL_gamecontroller.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/joystick/SDL_gamecontroller.c	Sat Dec 13 01:17:10 2014 -0500
@@ -89,6 +89,7 @@
 
 static ControllerMapping_t *s_pSupportedControllers = NULL;
 static ControllerMapping_t *s_pXInputMapping = NULL;
+static ControllerMapping_t *s_pEmscriptenMapping = NULL;
 
 /* The SDL game controller structure */
 struct _SDL_GameController
@@ -263,7 +264,13 @@
         return s_pXInputMapping;
     }
     else
-#endif /* SDL_JOYSTICK_XINPUT */
+#endif
+#if defined(SDL_JOYSTICK_EMSCRIPTEN)
+    if (s_pEmscriptenMapping) {
+        return s_pEmscriptenMapping;
+    }
+    else
+#endif
     {
         SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID(device_index);
         return SDL_PrivateGetControllerMappingForGUID(&jGUID);
@@ -668,6 +675,7 @@
     SDL_JoystickGUID jGUID;
     ControllerMapping_t *pControllerMapping;
     SDL_bool is_xinput_mapping = SDL_FALSE;
+    SDL_bool is_emscripten_mapping = SDL_FALSE;
 
     if (!mappingString) {
         return SDL_InvalidParamError("mappingString");
@@ -680,6 +688,9 @@
     if (!SDL_strcasecmp(pchGUID, "xinput")) {
         is_xinput_mapping = SDL_TRUE;
     }
+    if (!SDL_strcasecmp(pchGUID, "emscripten")) {
+        is_emscripten_mapping = SDL_TRUE;
+    }
     jGUID = SDL_JoystickGetGUIDFromString(pchGUID);
     SDL_free(pchGUID);
 
@@ -715,6 +726,9 @@
         if (is_xinput_mapping) {
             s_pXInputMapping = pControllerMapping;
         }
+        if (is_emscripten_mapping) {
+            s_pEmscriptenMapping = pControllerMapping;
+        }
         pControllerMapping->guid = jGUID;
         pControllerMapping->name = pchName;
         pControllerMapping->mapping = pchMapping;
--- a/src/joystick/SDL_gamecontrollerdb.h	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/joystick/SDL_gamecontrollerdb.h	Sat Dec 13 01:17:10 2014 -0500
@@ -76,6 +76,8 @@
 #endif
 #if defined(__ANDROID__)
     "4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+#elif defined(SDL_JOYSTICK_EMSCRIPTEN)
+    "emscripten,Standard Gamepad,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftx:a0,lefty:a1,rightx:a2,righty:a3,",
 #endif
     NULL
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/joystick/emscripten/SDL_sysjoystick.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,426 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_EMSCRIPTEN
+
+#include <stdio.h>              /* For the definition of NULL */
+#include "SDL_error.h"
+#include "SDL_events.h"
+
+#if !SDL_EVENTS_DISABLED
+#include "../../events/SDL_events_c.h"
+#endif
+
+#include "SDL_joystick.h"
+#include "SDL_hints.h"
+#include "SDL_assert.h"
+#include "SDL_timer.h"
+#include "SDL_log.h"
+#include "SDL_sysjoystick_c.h"
+#include "../SDL_joystick_c.h"
+
+static SDL_joylist_item * JoystickByIndex(int index);
+
+static SDL_joylist_item *SDL_joylist = NULL;
+static SDL_joylist_item *SDL_joylist_tail = NULL;
+static int numjoysticks = 0;
+static int instance_counter = 0;
+
+int
+Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
+{
+    int i;
+
+    SDL_joylist_item *item;
+
+    if (JoystickByIndex(gamepadEvent->index) != NULL) {
+      return 1;
+    }
+
+#if !SDL_EVENTS_DISABLED
+    SDL_Event event;
+#endif
+
+    item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
+    if (item == NULL) {
+        return 1;
+    }
+
+    SDL_zerop(item);
+    item->index = gamepadEvent->index;
+
+    item->name = SDL_strdup(gamepadEvent->id);
+    if ( item->name == NULL ) {
+        SDL_free(item);
+        return 1;
+    }
+
+    item->mapping = SDL_strdup(gamepadEvent->mapping);
+    if ( item->mapping == NULL ) {
+        SDL_free(item->name);
+        SDL_free(item);
+        return 1;
+    }
+
+    item->naxes = gamepadEvent->numAxes;
+    item->nbuttons = gamepadEvent->numButtons;
+    item->device_instance = instance_counter++;
+
+    item->timestamp = gamepadEvent->timestamp;
+
+    for( i = 0; i < item->naxes; i++) {
+        item->axis[i] = gamepadEvent->axis[i];
+    }
+
+    for( i = 0; i < item->nbuttons; i++) {
+        item->analogButton[i] = gamepadEvent->analogButton[i];
+        item->digitalButton[i] = gamepadEvent->digitalButton[i];
+    }
+
+    if (SDL_joylist_tail == NULL) {
+        SDL_joylist = SDL_joylist_tail = item;
+    } else {
+        SDL_joylist_tail->next = item;
+        SDL_joylist_tail = item;
+    }
+
+    ++numjoysticks;
+    SDL_Log("%d",numjoysticks);
+#if !SDL_EVENTS_DISABLED
+    event.type = SDL_JOYDEVICEADDED;
+
+    if (SDL_GetEventState(event.type) == SDL_ENABLE) {
+        event.jdevice.which = item->device_instance - 1;
+        if ( (SDL_EventOK == NULL) ||
+             (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
+            SDL_PushEvent(&event);
+        }
+    }
+#endif /* !SDL_EVENTS_DISABLED */
+
+    SDL_Log("Added joystick with index %d", item->index);
+
+    return 1;
+}
+
+int
+Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
+{
+    SDL_joylist_item *item = SDL_joylist;
+    SDL_joylist_item *prev = NULL;
+#if !SDL_EVENTS_DISABLED
+    SDL_Event event;
+#endif
+
+    while (item != NULL) {
+        if (item->index == gamepadEvent->index) {
+            break;
+        }
+        prev = item;
+        item = item->next;
+    }
+
+    if (item == NULL) {
+        return 1;
+    }
+
+    const int retval = item->device_instance;
+    if (item->joystick) {
+        item->joystick->hwdata = NULL;
+    }
+
+    if (prev != NULL) {
+        prev->next = item->next;
+    } else {
+        SDL_assert(SDL_joylist == item);
+        SDL_joylist = item->next;
+    }
+    if (item == SDL_joylist_tail) {
+        SDL_joylist_tail = prev;
+    }
+
+    /* Need to decrement the joystick count before we post the event */
+    --numjoysticks;
+
+#if !SDL_EVENTS_DISABLED
+    event.type = SDL_JOYDEVICEREMOVED;
+
+    if (SDL_GetEventState(event.type) == SDL_ENABLE) {
+        event.jdevice.which = item->device_instance;
+        if ( (SDL_EventOK == NULL) ||
+             (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
+            SDL_PushEvent(&event);
+        }
+    }
+#endif /* !SDL_EVENTS_DISABLED */
+
+    SDL_Log("Removed joystick with index %d", retval);
+    SDL_free(item->name);
+    SDL_free(item->mapping);
+    SDL_free(item);
+    return 1;
+}
+
+/* Function to scan the system for joysticks.
+ * It should return 0, or -1 on an unrecoverable fatal error.
+ */
+int
+SDL_SYS_JoystickInit(void)
+{
+    int retval, i, numjs;
+    EmscriptenGamepadEvent gamepadState;
+
+    numjoysticks = 0;
+    numjs = emscripten_get_num_gamepads();
+
+    /* Check if gamepad is supported by browser */
+    if (numjs == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
+        return -1;
+    }
+
+    /* handle already connected gamepads */
+    if (numjs > 0) {
+        for(i = 0; i < numjs; i++) {
+            retval = emscripten_get_gamepad_status(i, &gamepadState);
+            if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
+                Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
+                                             &gamepadState,
+                                             NULL);
+            }
+        }
+    }
+
+    retval = emscripten_set_gamepadconnected_callback(NULL,
+                                                      0,
+                                                      Emscripten_JoyStickConnected);
+
+    if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
+        return -1;
+    }
+
+    retval = emscripten_set_gamepaddisconnected_callback(NULL,
+                                                         0,
+                                                         Emscripten_JoyStickDisconnected);
+    if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static SDL_joylist_item *
+JoystickByIndex(int index)
+{
+    SDL_joylist_item *item = SDL_joylist;
+
+    if (index < 0) {
+        return NULL;
+    }
+
+    while (item != NULL) {
+        if (item->index == index) {
+            break;
+        }
+        item = item->next;
+    }
+
+    return item;
+}
+
+int SDL_SYS_NumJoysticks()
+{
+    return numjoysticks;
+}
+
+void SDL_SYS_JoystickDetect()
+{
+}
+
+// we need to poll to see if the gamepad state has changed
+SDL_bool SDL_SYS_JoystickNeedsPolling()
+{
+    return SDL_TRUE;
+}
+
+/* Function to get the device-dependent name of a joystick */
+const char *
+SDL_SYS_JoystickNameForDeviceIndex(int index)
+{
+    SDL_joylist_item *item = JoystickByIndex(index);
+    if (item == NULL) {
+        SDL_SetError("Joystick with index %d not found", index);
+        return NULL;
+    }
+
+    return item->name;
+}
+
+/* Function to perform the mapping from device index to the instance id for this index */
+SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int index)
+{
+    SDL_joylist_item *item = JoystickByIndex(index);
+    if (item == NULL) {
+        SDL_SetError("Joystick with index %d not found", index);
+        return NULL;
+    }
+
+    return item->device_instance;
+}
+
+/* Function to open a joystick for use.
+   The joystick to open is specified by the index field of the joystick.
+   This should fill the nbuttons and naxes fields of the joystick structure.
+   It returns 0, or -1 if there is an error.
+ */
+int
+SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int index)
+{
+    SDL_joylist_item *item = JoystickByIndex(index);
+
+    if (item == NULL ) {
+        return SDL_SetError("No such device");
+    }
+
+    if (item->joystick != NULL) {
+        return SDL_SetError("Joystick already opened");
+    }
+
+    joystick->instance_id = item->device_instance;
+    joystick->hwdata = (struct joystick_hwdata *) item;
+    item->joystick = joystick;
+
+    /* HTML5 Gamepad API doesn't say anything about these */
+    joystick->nhats = 0;
+    joystick->nballs = 0;
+
+    joystick->nbuttons = item->nbuttons;
+    joystick->naxes = item->naxes;
+
+    return (0);
+}
+
+/* Function to determine is this joystick is attached to the system right now */
+SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+{
+    return !joystick->closed && (joystick->hwdata != NULL);
+}
+
+/* Function to update the state of a joystick - called as a device poll.
+ * This function shouldn't update the joystick structure directly,
+ * but instead should call SDL_PrivateJoystick*() to deliver events
+ * and update joystick device state.
+ */
+void
+SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+{
+    EmscriptenGamepadEvent gamepadState;
+    SDL_joylist_item *item = SDL_joylist;
+    int i, result, button, buttonState;
+
+    while (item != NULL) {
+        result = emscripten_get_gamepad_status(item->index, &gamepadState);
+        if( result == EMSCRIPTEN_RESULT_SUCCESS) {
+            if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
+                for(i = 0; i < item->nbuttons; i++) {
+                    if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
+                        buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
+                        SDL_PrivateJoystickButton(item->joystick, i, buttonState);
+                    }
+                }
+
+                for(i = 0; i < item->naxes; i++) {
+                    if(item->axis[i] != gamepadState.axis[i]) {
+                        // do we need to do conversion?
+                        SDL_PrivateJoystickAxis(item->joystick, i,
+                                                  (Sint16) (32767.*gamepadState.axis[i]));
+                    }
+                }
+
+                item->timestamp = gamepadState.timestamp;
+                for( i = 0; i < item->naxes; i++) {
+                    item->axis[i] = gamepadState.axis[i];
+                }
+
+                for( i = 0; i < item->nbuttons; i++) {
+                    item->analogButton[i] = gamepadState.analogButton[i];
+                    item->digitalButton[i] = gamepadState.digitalButton[i];
+                }
+            }
+        }
+        item = item->next;
+    }
+}
+
+/* Function to close a joystick after use */
+void
+SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+{
+    if (joystick->hwdata) {
+        ((SDL_joylist_item*)joystick->hwdata)->joystick = NULL;
+        joystick->hwdata = NULL;
+    }
+    joystick->closed = 1;
+}
+
+/* Function to perform any system-specific joystick related cleanup */
+void
+SDL_SYS_JoystickQuit(void)
+{
+    SDL_joylist_item *item = NULL;
+    SDL_joylist_item *next = NULL;
+
+    for (item = SDL_joylist; item; item = next) {
+        next = item->next;
+        SDL_free(item->mapping);
+        SDL_free(item->name);
+        SDL_free(item);
+    }
+
+    SDL_joylist = SDL_joylist_tail = NULL;
+
+    numjoysticks = 0;
+    instance_counter = 0;
+}
+
+SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int index)
+{
+    SDL_JoystickGUID guid;
+    /* the GUID is just the first 16 chars of the name for now */
+    const char *name = SDL_SYS_JoystickNameForDeviceIndex(index);
+    SDL_zero(guid);
+    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    return guid;
+}
+
+
+SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
+{
+    SDL_JoystickGUID guid;
+    /* the GUID is just the first 16 chars of the name for now */
+    const char *name = joystick->name;
+    SDL_zero(guid);
+    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    return guid;
+}
+
+#endif /* SDL_JOYSTICK_EMSCRIPTEN */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/joystick/emscripten/SDL_sysjoystick_c.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,76 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty.  In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "SDL_config.h"
+
+#ifdef SDL_JOYSTICK_EMSCRIPTEN
+#include "../SDL_sysjoystick.h"
+
+
+#include <emscripten/html5.h>
+
+typedef enum
+{
+    EMSCRIPTEN_CONTROLLER_BUTTON_INVALID = -1,
+    EMSCRIPTEN_CONTROLLER_BUTTON_A,
+    EMSCRIPTEN_CONTROLLER_BUTTON_B,
+    EMSCRIPTEN_CONTROLLER_BUTTON_X,
+    EMSCRIPTEN_CONTROLLER_BUTTON_Y,
+    EMSCRIPTEN_CONTROLLER_BUTTON_L1,
+    EMSCRIPTEN_CONTROLLER_BUTTON_R1,
+    EMSCRIPTEN_CONTROLLER_BUTTON_L2,
+    EMSCRIPTEN_CONTROLLER_BUTTON_R2,
+    EMSCRIPTEN_CONTROLLER_BUTTON_BACK,
+    EMSCRIPTEN_CONTROLLER_BUTTON_START,
+    EMSCRIPTEN_CONTROLLER_BUTTON_LEFTSTICK,
+    EMSCRIPTEN_CONTROLLER_BUTTON_RIGHTSTICK,
+    EMSCRIPTEN_CONTROLLER_DPAD_UP,
+    EMSCRIPTEN_CONTROLLER_DPAD_DOWN,
+    EMSCRIPTEN_CONTROLLER_DPAD_LEFT,
+    EMSCRIPTEN_CONTROLLER_DPAD_RIGHT,
+    EMSCRIPTEN_CONTROLLER_BUTTON_GUIDE,
+} Emscripten_GameControllerButton;
+
+static int EMSCRIPTEN_MAX_NBUTTONS = 18;
+
+/* A linked list of available joysticks */
+typedef struct SDL_joylist_item
+{
+  int index;
+  char *name;
+  char *mapping;
+  SDL_JoystickID device_instance;
+  SDL_Joystick *joystick;
+  int nbuttons;
+  int naxes;
+  double timestamp;
+  double axis[64];
+  double analogButton[64];
+  EM_BOOL digitalButton[64];
+
+  struct SDL_joylist_item *next;
+} SDL_joylist_item;
+
+typedef SDL_joylist_item joystick_hwdata;
+
+#endif /* SDL_JOYSTICK_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/power/SDL_power.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/power/SDL_power.c	Sat Dec 13 01:17:10 2014 -0500
@@ -38,6 +38,7 @@
 SDL_bool SDL_GetPowerInfo_Android(SDL_PowerState *, int *, int *);
 SDL_bool SDL_GetPowerInfo_PSP(SDL_PowerState *, int *, int *);
 SDL_bool SDL_GetPowerInfo_WinRT(SDL_PowerState *, int *, int *);
+SDL_bool SDL_GetPowerInfo_Emscripten(SDL_PowerState *, int *, int *);
 
 #ifndef SDL_POWER_DISABLED
 #ifdef SDL_POWER_HARDWIRED
@@ -81,6 +82,9 @@
 #ifdef SDL_POWER_WINRT          /* handles WinRT */
     SDL_GetPowerInfo_WinRT,
 #endif
+#ifdef SDL_POWER_EMSCRIPTEN     /* handles Emscripten */
+    SDL_GetPowerInfo_Emscripten,
+#endif
 
 #ifdef SDL_POWER_HARDWIRED
     SDL_GetPowerInfo_Hardwired,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/power/emscripten/SDL_syspower.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,62 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef SDL_POWER_DISABLED
+#if SDL_POWER_EMSCRIPTEN
+
+#include <emscripten/html5.h>
+
+#include "SDL_power.h"
+
+SDL_bool
+SDL_GetPowerInfo_Emscripten(SDL_PowerState *state, int *seconds, int *percent)
+{
+    EmscriptenBatteryEvent batteryState;
+    int haveBattery = 0;
+
+    if (emscripten_get_battery_status(&batteryState) == EMSCRIPTEN_RESULT_NOT_SUPPORTED)
+        return SDL_FALSE;
+
+    haveBattery = batteryState.level != 1.0 || !batteryState.charging || batteryState.chargingTime != 0.0;
+
+    if (!haveBattery) {
+        *state = SDL_POWERSTATE_NO_BATTERY;
+        *seconds = -1;
+        *percent = -1;
+        return SDL_TRUE;
+    }
+
+    if (batteryState.charging)
+        *state = batteryState.chargingTime == 0.0 ? SDL_POWERSTATE_CHARGED : SDL_POWERSTATE_CHARGING;
+    else
+        *state = SDL_POWERSTATE_ON_BATTERY;
+
+    *seconds = batteryState.dischargingTime;
+    *percent = batteryState.level * 100;
+
+    return SDL_TRUE;
+}
+
+#endif /* SDL_POWER_EMSCRIPTEN */
+#endif /* SDL_POWER_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/src/render/opengles2/SDL_gles2funcs.h	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/render/opengles2/SDL_gles2funcs.h	Sat Dec 13 01:17:10 2014 -0500
@@ -69,3 +69,7 @@
 SDL_PROC(void, glDeleteFramebuffers, (GLsizei, const GLuint *))
 SDL_PROC(GLint, glGetAttribLocation, (GLuint, const GLchar *))
 SDL_PROC(void, glGetProgramInfoLog, (GLuint, GLsizei, GLsizei*, GLchar*))
+SDL_PROC(void, glGenBuffers, (GLsizei, GLuint *))
+SDL_PROC(void, glBindBuffer, (GLenum, GLuint))
+SDL_PROC(void, glBufferData, (GLenum, GLsizeiptr, const GLvoid *, GLenum))
+SDL_PROC(void, glBufferSubData, (GLenum, GLintptr, GLsizeiptr, const GLvoid *))
--- a/src/render/opengles2/SDL_render_gles2.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/render/opengles2/SDL_render_gles2.c	Sat Dec 13 01:17:10 2014 -0500
@@ -180,6 +180,9 @@
     GLES2_ProgramCache program_cache;
     GLES2_ProgramCacheEntry *current_program;
     Uint8 clear_r, clear_g, clear_b, clear_a;
+
+    GLuint vertex_buffers[4];
+    GLsizeiptr vertex_buffer_size[4];
 } GLES2_DriverContext;
 
 #define GLES2_MAX_CACHED_PROGRAMS 8
@@ -1386,6 +1389,32 @@
 }
 
 static int
+GLES2_UpdateVertexBuffer(SDL_Renderer *renderer, GLES2_Attribute attr,
+                         const void *vertexData, size_t dataSizeInBytes)
+{
+    GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
+#if 0
+    data->glVertexAttribPointer(attr, attr == GLES2_ATTRIBUTE_ANGLE ? 1 : 2, GL_FLOAT, GL_FALSE, 0, vertexData);
+#else
+
+    if (!data->vertex_buffers[attr])
+        data->glGenBuffers(1, &data->vertex_buffers[attr]);
+
+    data->glBindBuffer(GL_ARRAY_BUFFER, data->vertex_buffers[attr]);
+
+    if (data->vertex_buffer_size[attr] < dataSizeInBytes) {
+        data->glBufferData(GL_ARRAY_BUFFER, dataSizeInBytes, vertexData, GL_STREAM_DRAW);
+        data->vertex_buffer_size[attr] = dataSizeInBytes;
+    } else {
+        data->glBufferSubData(GL_ARRAY_BUFFER, 0, dataSizeInBytes, vertexData);
+    }
+
+    data->glVertexAttribPointer(attr, attr == GLES2_ATTRIBUTE_ANGLE ? 1 : 2, GL_FLOAT, GL_FALSE, 0, 0);
+#endif
+    return 0;
+}
+
+static int
 GLES2_RenderDrawPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count)
 {
     GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
@@ -1405,7 +1434,8 @@
         vertices[idx * 2] = x;
         vertices[(idx * 2) + 1] = y;
     }
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+    /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2 * sizeof(GLfloat));
     data->glDrawArrays(GL_POINTS, 0, count);
     SDL_stack_free(vertices);
     return 0;
@@ -1431,7 +1461,8 @@
         vertices[idx * 2] = x;
         vertices[(idx * 2) + 1] = y;
     }
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+    /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, count * 2 * sizeof(GLfloat));
     data->glDrawArrays(GL_LINE_STRIP, 0, count);
 
     /* We need to close the endpoint of the line */
@@ -1472,7 +1503,8 @@
         vertices[5] = yMax;
         vertices[6] = xMax;
         vertices[7] = yMax;
-        data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+        /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/
+        GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat));
         data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     }
     return GL_CheckError("", renderer);
@@ -1668,7 +1700,8 @@
     vertices[5] = (dstrect->y + dstrect->h);
     vertices[6] = (dstrect->x + dstrect->w);
     vertices[7] = (dstrect->y + dstrect->h);
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+    /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat));
     texCoords[0] = srcrect->x / (GLfloat)texture->w;
     texCoords[1] = srcrect->y / (GLfloat)texture->h;
     texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
@@ -1677,7 +1710,8 @@
     texCoords[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
     texCoords[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
     texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
+    /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);*/
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_TEXCOORD, texCoords, 8 * sizeof(GLfloat));
     data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
     return GL_CheckError("", renderer);
@@ -1727,9 +1761,13 @@
         vertices[5] = vertices[7] = tmp;
     }
 
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_ANGLE, 1, GL_FLOAT, GL_FALSE, 0, &fAngle);
+    /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_ANGLE, 1, GL_FLOAT, GL_FALSE, 0, &fAngle);
     data->glVertexAttribPointer(GLES2_ATTRIBUTE_CENTER, 2, GL_FLOAT, GL_FALSE, 0, translate);
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+    data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/
+
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_ANGLE, fAngle, 4 * sizeof(GLfloat));
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_CENTER, translate, 8 * sizeof(GLfloat));
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat));
 
     texCoords[0] = srcrect->x / (GLfloat)texture->w;
     texCoords[1] = srcrect->y / (GLfloat)texture->h;
@@ -1739,7 +1777,8 @@
     texCoords[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
     texCoords[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
     texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
-    data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
+    /*data->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);*/
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_TEXCOORD, texCoords, 8 * sizeof(GLfloat));
     data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_CENTER);
     data->glDisableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE);
--- a/src/video/SDL_sysvideo.h	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/video/SDL_sysvideo.h	Sat Dec 13 01:17:10 2014 -0500
@@ -394,6 +394,9 @@
 #if SDL_VIDEO_DRIVER_VIVANTE
 extern VideoBootStrap VIVANTE_bootstrap;
 #endif
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
+extern VideoBootStrap Emscripten_bootstrap;
+#endif
 
 extern SDL_VideoDevice *SDL_GetVideoDevice(void);
 extern int SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode);
--- a/src/video/SDL_video.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/src/video/SDL_video.c	Sat Dec 13 01:17:10 2014 -0500
@@ -98,6 +98,9 @@
 #if SDL_VIDEO_DRIVER_NACL
     &NACL_bootstrap,
 #endif
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
+    &Emscripten_bootstrap,
+#endif
 #if SDL_VIDEO_DRIVER_DUMMY
     &DUMMY_bootstrap,
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenevents.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,638 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
+
+#include <emscripten/html5.h>
+
+#include "../../events/SDL_events_c.h"
+#include "../../events/SDL_keyboard_c.h"
+#include "../../events/SDL_touch_c.h"
+
+#include "SDL_emscriptenevents.h"
+#include "SDL_emscriptenvideo.h"
+
+#include "SDL_hints.h"
+
+#define FULLSCREEN_MASK ( SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN )
+
+/*
+.which to scancode
+https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Constants
+*/
+static const SDL_Scancode emscripten_scancode_table[] = {
+    /*  0 */    SDL_SCANCODE_UNKNOWN,
+    /*  1 */    SDL_SCANCODE_UNKNOWN,
+    /*  2 */    SDL_SCANCODE_UNKNOWN,
+    /*  3 */    SDL_SCANCODE_CANCEL,
+    /*  4 */    SDL_SCANCODE_UNKNOWN,
+    /*  5 */    SDL_SCANCODE_UNKNOWN,
+    /*  6 */    SDL_SCANCODE_HELP,
+    /*  7 */    SDL_SCANCODE_UNKNOWN,
+    /*  8 */    SDL_SCANCODE_BACKSPACE,
+    /*  9 */    SDL_SCANCODE_TAB,
+    /*  10 */   SDL_SCANCODE_UNKNOWN,
+    /*  11 */   SDL_SCANCODE_UNKNOWN,
+    /*  12 */   SDL_SCANCODE_UNKNOWN,
+    /*  13 */   SDL_SCANCODE_RETURN,
+    /*  14 */   SDL_SCANCODE_UNKNOWN,
+    /*  15 */   SDL_SCANCODE_UNKNOWN,
+    /*  16 */   SDL_SCANCODE_LSHIFT,
+    /*  17 */   SDL_SCANCODE_LCTRL,
+    /*  18 */   SDL_SCANCODE_LALT,
+    /*  19 */   SDL_SCANCODE_PAUSE,
+    /*  20 */   SDL_SCANCODE_CAPSLOCK,
+    /*  21 */   SDL_SCANCODE_UNKNOWN,
+    /*  22 */   SDL_SCANCODE_UNKNOWN,
+    /*  23 */   SDL_SCANCODE_UNKNOWN,
+    /*  24 */   SDL_SCANCODE_UNKNOWN,
+    /*  25 */   SDL_SCANCODE_UNKNOWN,
+    /*  26 */   SDL_SCANCODE_UNKNOWN,
+    /*  27 */   SDL_SCANCODE_ESCAPE,
+    /*  28 */   SDL_SCANCODE_UNKNOWN,
+    /*  29 */   SDL_SCANCODE_UNKNOWN,
+    /*  30 */   SDL_SCANCODE_UNKNOWN,
+    /*  31 */   SDL_SCANCODE_UNKNOWN,
+    /*  32 */   SDL_SCANCODE_SPACE,
+    /*  33 */   SDL_SCANCODE_PAGEUP,
+    /*  34 */   SDL_SCANCODE_PAGEDOWN,
+    /*  35 */   SDL_SCANCODE_END,
+    /*  36 */   SDL_SCANCODE_HOME,
+    /*  37 */   SDL_SCANCODE_LEFT,
+    /*  38 */   SDL_SCANCODE_UP,
+    /*  39 */   SDL_SCANCODE_RIGHT,
+    /*  40 */   SDL_SCANCODE_DOWN,
+    /*  41 */   SDL_SCANCODE_UNKNOWN,
+    /*  42 */   SDL_SCANCODE_UNKNOWN,
+    /*  43 */   SDL_SCANCODE_UNKNOWN,
+    /*  44 */   SDL_SCANCODE_UNKNOWN,
+    /*  45 */   SDL_SCANCODE_INSERT,
+    /*  46 */   SDL_SCANCODE_DELETE,
+    /*  47 */   SDL_SCANCODE_UNKNOWN,
+    /*  48 */   SDL_SCANCODE_0,
+    /*  49 */   SDL_SCANCODE_1,
+    /*  50 */   SDL_SCANCODE_2,
+    /*  51 */   SDL_SCANCODE_3,
+    /*  52 */   SDL_SCANCODE_4,
+    /*  53 */   SDL_SCANCODE_5,
+    /*  54 */   SDL_SCANCODE_6,
+    /*  55 */   SDL_SCANCODE_7,
+    /*  56 */   SDL_SCANCODE_8,
+    /*  57 */   SDL_SCANCODE_9,
+    /*  58 */   SDL_SCANCODE_UNKNOWN,
+    /*  59 */   SDL_SCANCODE_SEMICOLON,
+    /*  60 */   SDL_SCANCODE_UNKNOWN,
+    /*  61 */   SDL_SCANCODE_EQUALS,
+    /*  62 */   SDL_SCANCODE_UNKNOWN,
+    /*  63 */   SDL_SCANCODE_UNKNOWN,
+    /*  64 */   SDL_SCANCODE_UNKNOWN,
+    /*  65 */   SDL_SCANCODE_A,
+    /*  66 */   SDL_SCANCODE_B,
+    /*  67 */   SDL_SCANCODE_C,
+    /*  68 */   SDL_SCANCODE_D,
+    /*  69 */   SDL_SCANCODE_E,
+    /*  70 */   SDL_SCANCODE_F,
+    /*  71 */   SDL_SCANCODE_G,
+    /*  72 */   SDL_SCANCODE_H,
+    /*  73 */   SDL_SCANCODE_I,
+    /*  74 */   SDL_SCANCODE_J,
+    /*  75 */   SDL_SCANCODE_K,
+    /*  76 */   SDL_SCANCODE_L,
+    /*  77 */   SDL_SCANCODE_M,
+    /*  78 */   SDL_SCANCODE_N,
+    /*  79 */   SDL_SCANCODE_O,
+    /*  80 */   SDL_SCANCODE_P,
+    /*  81 */   SDL_SCANCODE_Q,
+    /*  82 */   SDL_SCANCODE_R,
+    /*  83 */   SDL_SCANCODE_S,
+    /*  84 */   SDL_SCANCODE_T,
+    /*  85 */   SDL_SCANCODE_U,
+    /*  86 */   SDL_SCANCODE_V,
+    /*  87 */   SDL_SCANCODE_W,
+    /*  88 */   SDL_SCANCODE_X,
+    /*  89 */   SDL_SCANCODE_Y,
+    /*  90 */   SDL_SCANCODE_Z,
+    /*  91 */   SDL_SCANCODE_LGUI,
+    /*  92 */   SDL_SCANCODE_UNKNOWN,
+    /*  93 */   SDL_SCANCODE_APPLICATION,
+    /*  94 */   SDL_SCANCODE_UNKNOWN,
+    /*  95 */   SDL_SCANCODE_UNKNOWN,
+    /*  96 */   SDL_SCANCODE_KP_0,
+    /*  97 */   SDL_SCANCODE_KP_1,
+    /*  98 */   SDL_SCANCODE_KP_2,
+    /*  99 */   SDL_SCANCODE_KP_3,
+    /* 100 */   SDL_SCANCODE_KP_4,
+    /* 101 */   SDL_SCANCODE_KP_5,
+    /* 102 */   SDL_SCANCODE_KP_6,
+    /* 103 */   SDL_SCANCODE_KP_7,
+    /* 104 */   SDL_SCANCODE_KP_8,
+    /* 105 */   SDL_SCANCODE_KP_9,
+    /* 106 */   SDL_SCANCODE_KP_MULTIPLY,
+    /* 107 */   SDL_SCANCODE_KP_PLUS,
+    /* 108 */   SDL_SCANCODE_UNKNOWN,
+    /* 109 */   SDL_SCANCODE_KP_MINUS,
+    /* 110 */   SDL_SCANCODE_KP_PERIOD,
+    /* 111 */   SDL_SCANCODE_KP_DIVIDE,
+    /* 112 */   SDL_SCANCODE_F1,
+    /* 113 */   SDL_SCANCODE_F2,
+    /* 114 */   SDL_SCANCODE_F3,
+    /* 115 */   SDL_SCANCODE_F4,
+    /* 116 */   SDL_SCANCODE_F5,
+    /* 117 */   SDL_SCANCODE_F6,
+    /* 118 */   SDL_SCANCODE_F7,
+    /* 119 */   SDL_SCANCODE_F8,
+    /* 120 */   SDL_SCANCODE_F9,
+    /* 121 */   SDL_SCANCODE_F10,
+    /* 122 */   SDL_SCANCODE_F11,
+    /* 123 */   SDL_SCANCODE_F12,
+    /* 124 */   SDL_SCANCODE_F13,
+    /* 125 */   SDL_SCANCODE_F14,
+    /* 126 */   SDL_SCANCODE_F15,
+    /* 127 */   SDL_SCANCODE_F16,
+    /* 128 */   SDL_SCANCODE_F17,
+    /* 129 */   SDL_SCANCODE_F18,
+    /* 130 */   SDL_SCANCODE_F19,
+    /* 131 */   SDL_SCANCODE_F20,
+    /* 132 */   SDL_SCANCODE_F21,
+    /* 133 */   SDL_SCANCODE_F22,
+    /* 134 */   SDL_SCANCODE_F23,
+    /* 135 */   SDL_SCANCODE_F24,
+    /* 136 */   SDL_SCANCODE_UNKNOWN,
+    /* 137 */   SDL_SCANCODE_UNKNOWN,
+    /* 138 */   SDL_SCANCODE_UNKNOWN,
+    /* 139 */   SDL_SCANCODE_UNKNOWN,
+    /* 140 */   SDL_SCANCODE_UNKNOWN,
+    /* 141 */   SDL_SCANCODE_UNKNOWN,
+    /* 142 */   SDL_SCANCODE_UNKNOWN,
+    /* 143 */   SDL_SCANCODE_UNKNOWN,
+    /* 144 */   SDL_SCANCODE_NUMLOCKCLEAR,
+    /* 145 */   SDL_SCANCODE_SCROLLLOCK,
+    /* 146 */   SDL_SCANCODE_UNKNOWN,
+    /* 147 */   SDL_SCANCODE_UNKNOWN,
+    /* 148 */   SDL_SCANCODE_UNKNOWN,
+    /* 149 */   SDL_SCANCODE_UNKNOWN,
+    /* 150 */   SDL_SCANCODE_UNKNOWN,
+    /* 151 */   SDL_SCANCODE_UNKNOWN,
+    /* 152 */   SDL_SCANCODE_UNKNOWN,
+    /* 153 */   SDL_SCANCODE_UNKNOWN,
+    /* 154 */   SDL_SCANCODE_UNKNOWN,
+    /* 155 */   SDL_SCANCODE_UNKNOWN,
+    /* 156 */   SDL_SCANCODE_UNKNOWN,
+    /* 157 */   SDL_SCANCODE_UNKNOWN,
+    /* 158 */   SDL_SCANCODE_UNKNOWN,
+    /* 159 */   SDL_SCANCODE_UNKNOWN,
+    /* 160 */   SDL_SCANCODE_UNKNOWN,
+    /* 161 */   SDL_SCANCODE_UNKNOWN,
+    /* 162 */   SDL_SCANCODE_UNKNOWN,
+    /* 163 */   SDL_SCANCODE_UNKNOWN,
+    /* 164 */   SDL_SCANCODE_UNKNOWN,
+    /* 165 */   SDL_SCANCODE_UNKNOWN,
+    /* 166 */   SDL_SCANCODE_UNKNOWN,
+    /* 167 */   SDL_SCANCODE_UNKNOWN,
+    /* 168 */   SDL_SCANCODE_UNKNOWN,
+    /* 169 */   SDL_SCANCODE_UNKNOWN,
+    /* 170 */   SDL_SCANCODE_UNKNOWN,
+    /* 171 */   SDL_SCANCODE_UNKNOWN,
+    /* 172 */   SDL_SCANCODE_UNKNOWN,
+    /* 173 */   SDL_SCANCODE_MINUS, /*FX*/
+    /* 174 */   SDL_SCANCODE_UNKNOWN,
+    /* 175 */   SDL_SCANCODE_UNKNOWN,
+    /* 176 */   SDL_SCANCODE_UNKNOWN,
+    /* 177 */   SDL_SCANCODE_UNKNOWN,
+    /* 178 */   SDL_SCANCODE_UNKNOWN,
+    /* 179 */   SDL_SCANCODE_UNKNOWN,
+    /* 180 */   SDL_SCANCODE_UNKNOWN,
+    /* 181 */   SDL_SCANCODE_UNKNOWN,
+    /* 182 */   SDL_SCANCODE_UNKNOWN,
+    /* 183 */   SDL_SCANCODE_UNKNOWN,
+    /* 184 */   SDL_SCANCODE_UNKNOWN,
+    /* 185 */   SDL_SCANCODE_UNKNOWN,
+    /* 186 */   SDL_SCANCODE_SEMICOLON, /*IE, Chrome, D3E legacy*/
+    /* 187 */   SDL_SCANCODE_EQUALS, /*IE, Chrome, D3E legacy*/
+    /* 188 */   SDL_SCANCODE_COMMA,
+    /* 189 */   SDL_SCANCODE_MINUS, /*IE, Chrome, D3E legacy*/
+    /* 190 */   SDL_SCANCODE_PERIOD,
+    /* 191 */   SDL_SCANCODE_SLASH,
+    /* 192 */   SDL_SCANCODE_GRAVE, /*FX, D3E legacy (SDL_SCANCODE_APOSTROPHE in IE/Chrome)*/
+    /* 193 */   SDL_SCANCODE_UNKNOWN,
+    /* 194 */   SDL_SCANCODE_UNKNOWN,
+    /* 195 */   SDL_SCANCODE_UNKNOWN,
+    /* 196 */   SDL_SCANCODE_UNKNOWN,
+    /* 197 */   SDL_SCANCODE_UNKNOWN,
+    /* 198 */   SDL_SCANCODE_UNKNOWN,
+    /* 199 */   SDL_SCANCODE_UNKNOWN,
+    /* 200 */   SDL_SCANCODE_UNKNOWN,
+    /* 201 */   SDL_SCANCODE_UNKNOWN,
+    /* 202 */   SDL_SCANCODE_UNKNOWN,
+    /* 203 */   SDL_SCANCODE_UNKNOWN,
+    /* 204 */   SDL_SCANCODE_UNKNOWN,
+    /* 205 */   SDL_SCANCODE_UNKNOWN,
+    /* 206 */   SDL_SCANCODE_UNKNOWN,
+    /* 207 */   SDL_SCANCODE_UNKNOWN,
+    /* 208 */   SDL_SCANCODE_UNKNOWN,
+    /* 209 */   SDL_SCANCODE_UNKNOWN,
+    /* 210 */   SDL_SCANCODE_UNKNOWN,
+    /* 211 */   SDL_SCANCODE_UNKNOWN,
+    /* 212 */   SDL_SCANCODE_UNKNOWN,
+    /* 213 */   SDL_SCANCODE_UNKNOWN,
+    /* 214 */   SDL_SCANCODE_UNKNOWN,
+    /* 215 */   SDL_SCANCODE_UNKNOWN,
+    /* 216 */   SDL_SCANCODE_UNKNOWN,
+    /* 217 */   SDL_SCANCODE_UNKNOWN,
+    /* 218 */   SDL_SCANCODE_UNKNOWN,
+    /* 219 */   SDL_SCANCODE_LEFTBRACKET,
+    /* 220 */   SDL_SCANCODE_BACKSLASH,
+    /* 221 */   SDL_SCANCODE_RIGHTBRACKET,
+    /* 222 */   SDL_SCANCODE_APOSTROPHE, /*FX, D3E legacy*/
+};
+
+
+/* "borrowed" from SDL_windowsevents.c */
+int
+Emscripten_ConvertUTF32toUTF8(Uint32 codepoint, char * text)
+{
+    if (codepoint <= 0x7F) {
+        text[0] = (char) codepoint;
+        text[1] = '\0';
+    } else if (codepoint <= 0x7FF) {
+        text[0] = 0xC0 | (char) ((codepoint >> 6) & 0x1F);
+        text[1] = 0x80 | (char) (codepoint & 0x3F);
+        text[2] = '\0';
+    } else if (codepoint <= 0xFFFF) {
+        text[0] = 0xE0 | (char) ((codepoint >> 12) & 0x0F);
+        text[1] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
+        text[2] = 0x80 | (char) (codepoint & 0x3F);
+        text[3] = '\0';
+    } else if (codepoint <= 0x10FFFF) {
+        text[0] = 0xF0 | (char) ((codepoint >> 18) & 0x0F);
+        text[1] = 0x80 | (char) ((codepoint >> 12) & 0x3F);
+        text[2] = 0x80 | (char) ((codepoint >> 6) & 0x3F);
+        text[3] = 0x80 | (char) (codepoint & 0x3F);
+        text[4] = '\0';
+    } else {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
+int
+Emscripten_HandleMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    int mx = mouseEvent->canvasX, my = mouseEvent->canvasY;
+    EmscriptenPointerlockChangeEvent pointerlock_status;
+
+    /* check for pointer lock */
+    emscripten_get_pointerlock_status(&pointerlock_status);
+
+    if (pointerlock_status.isActive) {
+        mx = mouseEvent->movementX;
+        my = mouseEvent->movementY;
+    }
+
+    /* rescale (in case canvas is being scaled)*/
+    double client_w, client_h;
+    emscripten_get_element_css_size(NULL, &client_w, &client_h);
+
+    mx = mx * (window_data->window->w / (client_w * window_data->pixel_ratio));
+    my = my * (window_data->window->h / (client_h * window_data->pixel_ratio));
+
+    SDL_SendMouseMotion(window_data->window, 0, pointerlock_status.isActive, mx, my);
+    return 0;
+}
+
+int
+Emscripten_HandleMouseButton(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    uint32_t sdl_button;
+    switch (mouseEvent->button) {
+        case 0:
+            sdl_button = SDL_BUTTON_LEFT;
+            break;
+        case 1:
+            sdl_button = SDL_BUTTON_MIDDLE;
+            break;
+        case 2:
+            sdl_button = SDL_BUTTON_RIGHT;
+            break;
+        default:
+            return 0;
+    }
+    SDL_SendMouseButton(window_data->window, 0, eventType == EMSCRIPTEN_EVENT_MOUSEDOWN ? SDL_PRESSED : SDL_RELEASED, sdl_button);
+    return 1;
+}
+
+int
+Emscripten_HandleMouseFocus(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? SDL_WINDOWEVENT_ENTER : SDL_WINDOWEVENT_LEAVE, 0, 0);
+    return 1;
+}
+
+int
+Emscripten_HandleWheel(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    SDL_SendMouseWheel(window_data->window, 0, wheelEvent->deltaX, -wheelEvent->deltaY);
+    return 1;
+}
+
+int
+Emscripten_HandleFocus(int eventType, const EmscriptenFocusEvent *wheelEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    SDL_SendWindowEvent(window_data->window, eventType == EMSCRIPTEN_EVENT_FOCUS ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
+    return 1;
+}
+
+int
+Emscripten_HandleTouch(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+{
+    /*SDL_WindowData *window_data = userData;*/
+    int i;
+
+    SDL_TouchID deviceId = 0;
+    if (!SDL_GetTouch(deviceId)) {
+        if (SDL_AddTouch(deviceId, "") < 0) {
+             return 0;
+        }
+    }
+
+    for (i = 0; i < touchEvent->numTouches; i++) {
+        long x, y, id;
+
+        if (!touchEvent->touches[i].isChanged)
+            continue;
+
+        id = touchEvent->touches[i].identifier;
+        x = touchEvent->touches[i].canvasX;
+        y = touchEvent->touches[i].canvasY;
+
+        if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) {
+            SDL_SendTouchMotion(deviceId, id, x, y, 1.0f);
+        } else if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) {
+            SDL_SendTouch(deviceId, id, SDL_TRUE, x, y, 1.0f);
+        } else {
+            SDL_SendTouch(deviceId, id, SDL_FALSE, x, y, 1.0f);
+        }
+    }
+
+
+    return 1;
+}
+
+int
+Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
+{
+    Uint32 scancode;
+
+    /* .keyCode is deprecated, but still the most reliable way to get keys */
+    if (keyEvent->keyCode < SDL_arraysize(emscripten_scancode_table)) {
+        scancode = emscripten_scancode_table[keyEvent->keyCode];
+
+        if (scancode != SDL_SCANCODE_UNKNOWN) {
+
+            if (keyEvent->location == DOM_KEY_LOCATION_RIGHT) {
+                switch (scancode) {
+                    case SDL_SCANCODE_LSHIFT:
+                        scancode = SDL_SCANCODE_RSHIFT;
+                        break;
+                    case SDL_SCANCODE_LCTRL:
+                        scancode = SDL_SCANCODE_RCTRL;
+                        break;
+                    case SDL_SCANCODE_LALT:
+                        scancode = SDL_SCANCODE_RALT;
+                        break;
+                    case SDL_SCANCODE_LGUI:
+                        scancode = SDL_SCANCODE_RGUI;
+                        break;
+                }
+            }
+            SDL_SendKeyboardKey(eventType == EMSCRIPTEN_EVENT_KEYDOWN ?
+                                SDL_PRESSED : SDL_RELEASED, scancode);
+        }
+    }
+
+    /* if we prevent keydown, we won't get keypress
+     * also we need to ALWAYS prevent backspace and tab otherwise chrome takes action and does bad navigation UX
+     */
+    return SDL_GetEventState(SDL_TEXTINPUT) != SDL_ENABLE || eventType != EMSCRIPTEN_EVENT_KEYDOWN
+            || keyEvent->keyCode == 8 /* backspace */ || keyEvent->keyCode == 9 /* tab */;
+}
+
+int
+Emscripten_HandleKeyPress(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
+{
+    char text[5];
+    Emscripten_ConvertUTF32toUTF8(keyEvent->charCode, text);
+    SDL_SendKeyboardText(text);
+    return 1;
+}
+
+int
+Emscripten_HandleFullscreenChange(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)
+{
+    /*make sure this is actually our element going fullscreen*/
+    if(SDL_strcmp(fullscreenChangeEvent->id, "SDLFullscreenElement") != 0)
+        return 0;
+
+    SDL_WindowData *window_data = userData;
+    if(fullscreenChangeEvent->isFullscreen)
+    {
+        SDL_bool is_desktop_fullscreen;
+        window_data->window->flags |= window_data->requested_fullscreen_mode;
+
+        if(!window_data->requested_fullscreen_mode)
+            window_data->window->flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; /*we didn't reqest fullscreen*/
+
+        window_data->requested_fullscreen_mode = 0;
+
+        is_desktop_fullscreen = (window_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
+
+        /*update size*/
+        if(window_data->window->flags & SDL_WINDOW_RESIZABLE || is_desktop_fullscreen)
+        {
+            emscripten_set_canvas_size(fullscreenChangeEvent->screenWidth, fullscreenChangeEvent->screenHeight);
+            SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, fullscreenChangeEvent->screenWidth, fullscreenChangeEvent->screenHeight);
+        }
+        else
+        {
+            /*preserve ratio*/
+            double w = window_data->window->w;
+            double h = window_data->window->h;
+            double factor = SDL_min(fullscreenChangeEvent->screenWidth / w, fullscreenChangeEvent->screenHeight / h);
+            emscripten_set_element_css_size(NULL, w * factor, h * factor);
+        }
+    }
+    else
+    {
+        EM_ASM({
+            //un-reparent canvas (similar to Module.requestFullscreen)
+            var canvas = Module['canvas'];
+            if(canvas.parentNode.id == "SDLFullscreenElement") {
+                var canvasContainer = canvas.parentNode;
+                canvasContainer.parentNode.insertBefore(canvas, canvasContainer);
+                canvasContainer.parentNode.removeChild(canvasContainer);
+            }
+        });
+        double unscaled_w = window_data->windowed_width / window_data->pixel_ratio;
+        double unscaled_h = window_data->windowed_height / window_data->pixel_ratio;
+        emscripten_set_canvas_size(window_data->windowed_width, window_data->windowed_height);
+
+        if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
+            emscripten_set_element_css_size(NULL, unscaled_w, unscaled_h);
+        }
+
+        SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, unscaled_w, unscaled_h);
+
+        window_data->window->flags &= ~FULLSCREEN_MASK;
+    }
+
+    return 0;
+}
+
+int
+Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    if(window_data->window->flags & FULLSCREEN_MASK)
+    {
+        SDL_bool is_desktop_fullscreen = (window_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP;
+
+        if(window_data->window->flags & SDL_WINDOW_RESIZABLE || is_desktop_fullscreen)
+        {
+            emscripten_set_canvas_size(uiEvent->windowInnerWidth * window_data->pixel_ratio, uiEvent->windowInnerHeight * window_data->pixel_ratio);
+            SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, uiEvent->windowInnerWidth, uiEvent->windowInnerHeight);
+        }
+    }
+    else
+    {
+        /* this will only work if the canvas size is set through css */
+        if(window_data->window->flags & SDL_WINDOW_RESIZABLE)
+        {
+            double w = window_data->window->w;
+            double h = window_data->window->h;
+
+            if(window_data->external_size) {
+                emscripten_get_element_css_size(NULL, &w, &h);
+            }
+
+            emscripten_set_canvas_size(w * window_data->pixel_ratio, h * window_data->pixel_ratio);
+
+            /* set_canvas_size unsets this */
+            if (!window_data->external_size && window_data->pixel_ratio != 1.0f) {
+                emscripten_set_element_css_size(NULL, w, h);
+            }
+
+            SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
+        }
+    }
+
+    return 0;
+}
+
+int
+Emscripten_HandleVisibilityChange(int eventType, const EmscriptenVisibilityChangeEvent *visEvent, void *userData)
+{
+    SDL_WindowData *window_data = userData;
+    SDL_SendWindowEvent(window_data->window, visEvent->hidden ? SDL_WINDOWEVENT_HIDDEN : SDL_WINDOWEVENT_SHOWN, 0, 0);
+    return 0;
+}
+
+void
+Emscripten_RegisterEventHandlers(SDL_WindowData *data)
+{
+    /* There is only one window and that window is the canvas */
+    emscripten_set_mousemove_callback("#canvas", data, 0, Emscripten_HandleMouseMove);
+
+    emscripten_set_mousedown_callback("#canvas", data, 0, Emscripten_HandleMouseButton);
+    emscripten_set_mouseup_callback("#canvas", data, 0, Emscripten_HandleMouseButton);
+
+    emscripten_set_mouseenter_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
+    emscripten_set_mouseleave_callback("#canvas", data, 0, Emscripten_HandleMouseFocus);
+
+    emscripten_set_wheel_callback("#canvas", data, 0, Emscripten_HandleWheel);
+
+    emscripten_set_focus_callback("#canvas", data, 0, Emscripten_HandleFocus);
+    emscripten_set_blur_callback("#canvas", data, 0, Emscripten_HandleFocus);
+
+    emscripten_set_touchstart_callback("#canvas", data, 0, Emscripten_HandleTouch);
+    emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch);
+    emscripten_set_touchmove_callback("#canvas", data, 0, Emscripten_HandleTouch);
+    emscripten_set_touchcancel_callback("#canvas", data, 0, Emscripten_HandleTouch);
+
+    /* Keyboard events are awkward */
+    const char *keyElement = SDL_GetHint(SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT);
+    if (!keyElement) keyElement = "#window";
+
+    emscripten_set_keydown_callback(keyElement, data, 0, Emscripten_HandleKey);
+    emscripten_set_keyup_callback(keyElement, data, 0, Emscripten_HandleKey);
+    emscripten_set_keypress_callback(keyElement, data, 0, Emscripten_HandleKeyPress);
+
+    emscripten_set_fullscreenchange_callback("#document", data, 0, Emscripten_HandleFullscreenChange);
+
+    emscripten_set_resize_callback("#window", data, 0, Emscripten_HandleResize);
+
+    emscripten_set_visibilitychange_callback(data, 0, Emscripten_HandleVisibilityChange);
+}
+
+void
+Emscripten_UnregisterEventHandlers(SDL_WindowData *data)
+{
+    /* only works due to having one window */
+    emscripten_set_mousemove_callback("#canvas", NULL, 0, NULL);
+
+    emscripten_set_mousedown_callback("#canvas", NULL, 0, NULL);
+    emscripten_set_mouseup_callback("#canvas", NULL, 0, NULL);
+
+    emscripten_set_mouseenter_callback("#canvas", NULL, 0, NULL);
+    emscripten_set_mouseleave_callback("#canvas", NULL, 0, NULL);
+
+    emscripten_set_wheel_callback("#canvas", NULL, 0, NULL);
+
+    emscripten_set_focus_callback("#canvas", NULL, 0, NULL);
+    emscripten_set_blur_callback("#canvas", NULL, 0, NULL);
+
+    emscripten_set_touchstart_callback("#canvas", NULL, 0, NULL);
+    emscripten_set_touchend_callback("#canvas", NULL, 0, NULL);
+    emscripten_set_touchmove_callback("#canvas", NULL, 0, NULL);
+    emscripten_set_touchcancel_callback("#canvas", NULL, 0, NULL);
+
+    emscripten_set_keydown_callback("#window", NULL, 0, NULL);
+    emscripten_set_keyup_callback("#window", NULL, 0, NULL);
+
+    emscripten_set_keypress_callback("#window", NULL, 0, NULL);
+
+    emscripten_set_fullscreenchange_callback("#document", NULL, 0, NULL);
+
+    emscripten_set_resize_callback("#window", NULL, 0, NULL);
+
+    emscripten_set_visibilitychange_callback(NULL, 0, NULL);
+}
+
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenevents.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,36 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+#ifndef _SDL_emscriptenevents_h
+#define _SDL_emscriptenevents_h
+
+#include "SDL_emscriptenvideo.h"
+
+extern void
+Emscripten_RegisterEventHandlers(SDL_WindowData *data);
+
+extern void
+Emscripten_UnregisterEventHandlers(SDL_WindowData *data);
+#endif /* _SDL_emscriptenevents_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenframebuffer.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,136 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
+
+#include "SDL_emscriptenvideo.h"
+#include "SDL_emscriptenframebuffer.h"
+
+
+int Emscripten_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch)
+{
+    SDL_Surface *surface;
+    const Uint32 surface_format = SDL_PIXELFORMAT_BGR888;
+    int w, h;
+    int bpp;
+    Uint32 Rmask, Gmask, Bmask, Amask;
+
+    /* Free the old framebuffer surface */
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    surface = data->surface;
+    SDL_FreeSurface(surface);
+
+    /* Create a new one */
+    SDL_PixelFormatEnumToMasks(surface_format, &bpp, &Rmask, &Gmask, &Bmask, &Amask);
+    SDL_GetWindowSize(window, &w, &h);
+
+    surface = SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
+    if (!surface) {
+        return -1;
+    }
+
+    /* Save the info and return! */
+    data->surface = surface;
+    *format = surface_format;
+    *pixels = surface->pixels;
+    *pitch = surface->pitch;
+    return 0;
+}
+
+int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects)
+{
+    static int frame_number;
+    SDL_Surface *surface;
+
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    surface = data->surface;
+    if (!surface) {
+        return SDL_SetError("Couldn't find dummy surface for window");
+    }
+
+    /* Send the data to the display */
+
+    EM_ASM_INT({
+        //TODO: don't create context every update
+        var ctx = Module['canvas'].getContext('2d');
+
+        //library_sdl.js SDL_UnlockSurface
+        var image = ctx.createImageData($0, $1);
+        var data = image.data;
+        var src = $2 >> 2;
+        var dst = 0;
+        var isScreen = true;
+        var num;
+        if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) {
+            // IE10/IE11: ImageData objects are backed by the deprecated CanvasPixelArray,
+            // not UInt8ClampedArray. These don't have buffers, so we need to revert
+            // to copying a byte at a time. We do the undefined check because modern
+            // browsers do not define CanvasPixelArray anymore.
+            num = data.length;
+            while (dst < num) {
+                var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
+                data[dst  ] = val & 0xff;
+                data[dst+1] = (val >> 8) & 0xff;
+                data[dst+2] = (val >> 16) & 0xff;
+                data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
+                src++;
+                dst += 4;
+            }
+        } else {
+            var data32 = new Uint32Array(data.buffer);
+            num = data32.length;
+            if (isScreen) {
+                while (dst < num) {
+                    // HEAP32[src++] is an optimization. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
+                    data32[dst++] = HEAP32[src++] | 0xff000000;
+                }
+            } else {
+                while (dst < num) {
+                    data32[dst++] = HEAP32[src++];
+                }
+            }
+        }
+
+        ctx.putImageData(image, 0, 0);
+        return 0;
+    }, surface->w, surface->h, surface->pixels);
+
+    /*if (SDL_getenv("SDL_VIDEO_Emscripten_SAVE_FRAMES")) {
+        char file[128];
+        SDL_snprintf(file, sizeof(file), "SDL_window%d-%8.8d.bmp",
+                     SDL_GetWindowID(window), ++frame_number);
+        SDL_SaveBMP(surface, file);
+    }*/
+    return 0;
+}
+
+void Emscripten_DestroyWindowFramebuffer(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+
+    SDL_FreeSurface(data->surface);
+    data->surface = NULL;
+}
+
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenframebuffer.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,32 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_emscriptenframebuffer_h
+#define _SDL_emscriptenframebuffer_h
+
+extern int Emscripten_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
+extern int Emscripten_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
+extern void Emscripten_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
+
+#endif /* _SDL_emsctiptenframebuffer_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenmouse.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,218 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
+
+#include <emscripten/emscripten.h>
+#include <emscripten/html5.h>
+
+#include "SDL_emscriptenmouse.h"
+
+#include "../../events/SDL_mouse_c.h"
+#include "SDL_assert.h"
+
+
+static SDL_Cursor*
+Emscripten_CreateDefaultCursor()
+{
+    SDL_Cursor* cursor;
+    Emscripten_CursorData *curdata;
+
+    cursor = SDL_calloc(1, sizeof(SDL_Cursor));
+    if (cursor) {
+        curdata = (Emscripten_CursorData *) SDL_calloc(1, sizeof(*curdata));
+
+        curdata->system_cursor = "default";
+        cursor->driverdata = curdata;
+    }
+    else {
+        SDL_OutOfMemory();
+    }
+
+    return cursor;
+}
+
+static SDL_Cursor*
+Emscripten_CreateCursor(SDL_Surface* sruface, int hot_x, int hot_y)
+{
+    return Emscripten_CreateDefaultCursor();
+}
+
+static SDL_Cursor*
+Emscripten_CreateSystemCursor(SDL_SystemCursor id)
+{
+    SDL_Cursor *cursor;
+    Emscripten_CursorData *curdata;
+    const char *cursor_name = NULL;
+
+    switch(id) {
+        case SDL_SYSTEM_CURSOR_ARROW:
+            cursor_name = "default";
+            break;
+        case SDL_SYSTEM_CURSOR_IBEAM:
+            cursor_name = "text";
+            break;
+        case SDL_SYSTEM_CURSOR_WAIT:
+            cursor_name = "wait";
+            break;
+        case SDL_SYSTEM_CURSOR_CROSSHAIR:
+            cursor_name = "crosshair";
+            break;
+        case SDL_SYSTEM_CURSOR_WAITARROW:
+            cursor_name = "progress";
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENWSE:
+            cursor_name = "nwse-resize";
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENESW:
+            cursor_name = "nesw-resize";
+            break;
+        case SDL_SYSTEM_CURSOR_SIZEWE:
+            cursor_name = "ew-resize";
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENS:
+            cursor_name = "ns-resize";
+            break;
+        case SDL_SYSTEM_CURSOR_SIZEALL:
+            break;
+        case SDL_SYSTEM_CURSOR_NO:
+            cursor_name = "not-allowed";
+            break;
+        case SDL_SYSTEM_CURSOR_HAND:
+            cursor_name = "pointer";
+            break;
+        default:
+            SDL_assert(0);
+            return NULL;
+    }
+
+    cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
+    curdata = (Emscripten_CursorData *) SDL_calloc(1, sizeof(*curdata));
+
+    curdata->system_cursor = cursor_name;
+    cursor->driverdata = curdata;
+
+    return cursor;
+}
+
+static void
+Emscripten_FreeCursor(SDL_Cursor* cursor)
+{
+    Emscripten_CursorData *curdata;
+    if (cursor) {
+        curdata = (Emscripten_CursorData *) cursor->driverdata;
+
+        if (curdata != NULL) {
+            SDL_free(cursor->driverdata);
+        }
+
+        SDL_free(cursor);
+    }
+}
+
+static int
+Emscripten_ShowCursor(SDL_Cursor* cursor)
+{
+    Emscripten_CursorData *curdata;
+    if (SDL_GetMouseFocus() != NULL) {
+        if(cursor && cursor->driverdata) {
+            curdata = (Emscripten_CursorData *) cursor->driverdata;
+
+            if(curdata->system_cursor) {
+                EM_ASM_INT({
+                    if (Module['canvas']) {
+                        Module['canvas'].style['cursor'] = Module['Pointer_stringify']($0);
+                    }
+                    return 0;
+                }, curdata->system_cursor);
+            }
+        }
+        else {
+            EM_ASM(
+                if (Module['canvas']) {
+                    Module['canvas'].style['cursor'] = 'none';
+                }
+            );
+        }
+    }
+    return 0;
+}
+
+static void
+Emscripten_WarpMouse(SDL_Window* window, int x, int y)
+{
+    SDL_Unsupported();
+}
+
+static int
+Emscripten_SetRelativeMouseMode(SDL_bool enabled)
+{
+    /* TODO: pointer lock isn't actually enabled yet */
+    if(enabled) {
+        if(emscripten_request_pointerlock(NULL, 1) >= EMSCRIPTEN_RESULT_SUCCESS) {
+            return 0;
+        }
+    } else {
+        if(emscripten_exit_pointerlock() >= EMSCRIPTEN_RESULT_SUCCESS) {
+            return 0;
+        }
+    }
+    return -1;
+}
+
+void
+Emscripten_InitMouse()
+{
+    SDL_Mouse* mouse = SDL_GetMouse();
+
+    mouse->CreateCursor         = Emscripten_CreateCursor;
+    mouse->ShowCursor           = Emscripten_ShowCursor;
+    mouse->FreeCursor           = Emscripten_FreeCursor;
+    mouse->WarpMouse            = Emscripten_WarpMouse;
+    mouse->CreateSystemCursor   = Emscripten_CreateSystemCursor;
+    mouse->SetRelativeMouseMode = Emscripten_SetRelativeMouseMode;
+
+    SDL_SetDefaultCursor(Emscripten_CreateDefaultCursor());
+}
+
+void
+Emscripten_FiniMouse()
+{
+    SDL_Mouse* mouse = SDL_GetMouse();
+
+    Emscripten_FreeCursor(mouse->def_cursor);
+    mouse->def_cursor = NULL;
+
+    mouse->CreateCursor         = NULL;
+    mouse->ShowCursor           = NULL;
+    mouse->FreeCursor           = NULL;
+    mouse->WarpMouse            = NULL;
+    mouse->CreateSystemCursor   = NULL;
+    mouse->SetRelativeMouseMode = NULL;
+}
+
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenmouse.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,39 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+#ifndef _SDL_emscriptenmouse_h
+#define _SDL_emscriptenmouse_h
+
+typedef struct _Emscripten_CursorData
+{
+    const char *system_cursor;
+} Emscripten_CursorData;
+
+extern void
+Emscripten_InitMouse();
+
+extern void
+Emscripten_FiniMouse();
+
+#endif /* _SDL_emscriptenmouse_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenopengles.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,117 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL
+
+#include <emscripten/emscripten.h>
+#include <GLES2/gl2.h>
+
+#include "SDL_emscriptenvideo.h"
+#include "SDL_emscriptenopengles.h"
+
+#define LOAD_FUNC(NAME) _this->egl_data->NAME = NAME;
+
+/* EGL implementation of SDL OpenGL support */
+
+int
+Emscripten_GLES_LoadLibrary(_THIS, const char *path) {
+    /*we can't load EGL dynamically*/
+    _this->egl_data = (struct SDL_EGL_VideoData *) SDL_calloc(1, sizeof(SDL_EGL_VideoData));
+    if (!_this->egl_data) {
+        return SDL_OutOfMemory();
+    }
+    
+    LOAD_FUNC(eglGetDisplay);
+    LOAD_FUNC(eglInitialize);
+    LOAD_FUNC(eglTerminate);
+    LOAD_FUNC(eglGetProcAddress);
+    LOAD_FUNC(eglChooseConfig);
+    LOAD_FUNC(eglGetConfigAttrib);
+    LOAD_FUNC(eglCreateContext);
+    LOAD_FUNC(eglDestroyContext);
+    LOAD_FUNC(eglCreateWindowSurface);
+    LOAD_FUNC(eglDestroySurface);
+    LOAD_FUNC(eglMakeCurrent);
+    LOAD_FUNC(eglSwapBuffers);
+    LOAD_FUNC(eglSwapInterval);
+    LOAD_FUNC(eglWaitNative);
+    LOAD_FUNC(eglWaitGL);
+    LOAD_FUNC(eglBindAPI);
+    
+    _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (!_this->egl_data->egl_display) {
+        return SDL_SetError("Could not get EGL display");
+    }
+    
+    if (_this->egl_data->eglInitialize(_this->egl_data->egl_display, NULL, NULL) != EGL_TRUE) {
+        return SDL_SetError("Could not initialize EGL");
+    }
+
+    _this->gl_config.driver_loaded = 1;
+
+    if (path) {
+        SDL_strlcpy(_this->gl_config.driver_path, path, sizeof(_this->gl_config.driver_path) - 1);
+    } else {
+        *_this->gl_config.driver_path = '\0';
+    }
+    
+    return 0;
+}
+
+void
+Emscripten_GLES_DeleteContext(_THIS, SDL_GLContext context)
+{
+    /*
+    WebGL contexts can't actually be deleted, so we need to reset it.
+    ES2 renderer resets state on init anyway, clearing the canvas should be enough
+    */
+
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+    SDL_EGL_DeleteContext(_this, context);
+}
+
+SDL_EGL_CreateContext_impl(Emscripten)
+SDL_EGL_SwapWindow_impl(Emscripten)
+SDL_EGL_MakeCurrent_impl(Emscripten)
+
+void
+Emscripten_GLES_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
+{
+    SDL_WindowData *data;
+    if (window->driverdata) {
+        data = (SDL_WindowData *) window->driverdata;
+
+        if (w) {
+            *w = window->w * data->pixel_ratio;
+        }
+
+        if (h) {
+            *h = window->h * data->pixel_ratio;
+        }
+    }
+}
+
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL */
+
+/* vi: set ts=4 sw=4 expandtab: */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenopengles.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,49 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_emscriptenopengles_h
+#define _SDL_emscriptenopengles_h
+
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL
+
+#include "../SDL_sysvideo.h"
+#include "../SDL_egl_c.h"
+
+/* OpenGLES functions */
+#define Emscripten_GLES_GetAttribute SDL_EGL_GetAttribute
+#define Emscripten_GLES_GetProcAddress SDL_EGL_GetProcAddress
+#define Emscripten_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
+#define Emscripten_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
+#define Emscripten_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
+
+extern int Emscripten_GLES_LoadLibrary(_THIS, const char *path);
+extern void Emscripten_GLES_DeleteContext(_THIS, SDL_GLContext context);
+extern SDL_GLContext Emscripten_GLES_CreateContext(_THIS, SDL_Window * window);
+extern void Emscripten_GLES_SwapWindow(_THIS, SDL_Window * window);
+extern int Emscripten_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context);
+extern void Emscripten_GLES_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h);
+
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN && SDL_VIDEO_OPENGL_EGL */
+
+#endif /* _SDL_emscriptenopengles_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenvideo.c	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,320 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_EMSCRIPTEN
+
+#include "SDL_video.h"
+#include "SDL_mouse.h"
+#include "../SDL_sysvideo.h"
+#include "../SDL_pixels_c.h"
+#include "../SDL_egl_c.h"
+#include "../../events/SDL_events_c.h"
+
+#include "SDL_emscriptenvideo.h"
+#include "SDL_emscriptenopengles.h"
+#include "SDL_emscriptenframebuffer.h"
+#include "SDL_emscriptenevents.h"
+#include "SDL_emscriptenmouse.h"
+
+#define EMSCRIPTENVID_DRIVER_NAME "emscripten"
+
+/* Initialization/Query functions */
+static int Emscripten_VideoInit(_THIS);
+static int Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
+static void Emscripten_VideoQuit(_THIS);
+
+static int Emscripten_CreateWindow(_THIS, SDL_Window * window);
+static void Emscripten_SetWindowSize(_THIS, SDL_Window * window);
+static void Emscripten_DestroyWindow(_THIS, SDL_Window * window);
+static void Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
+static void Emscripten_PumpEvents(_THIS);
+
+
+/* Emscripten driver bootstrap functions */
+
+static int
+Emscripten_Available(void)
+{
+    return (1);
+}
+
+static void
+Emscripten_DeleteDevice(SDL_VideoDevice * device)
+{
+    SDL_free(device);
+}
+
+static SDL_VideoDevice *
+Emscripten_CreateDevice(int devindex)
+{
+    SDL_VideoDevice *device;
+
+    /* Initialize all variables that we clean on shutdown */
+    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
+    if (!device) {
+        SDL_OutOfMemory();
+        SDL_free(device);
+        return (0);
+    }
+
+    /* Set the function pointers */
+    device->VideoInit = Emscripten_VideoInit;
+    device->VideoQuit = Emscripten_VideoQuit;
+    device->SetDisplayMode = Emscripten_SetDisplayMode;
+
+
+    device->PumpEvents = Emscripten_PumpEvents;
+
+    device->CreateWindow = Emscripten_CreateWindow;
+    /*device->CreateWindowFrom = Emscripten_CreateWindowFrom;
+    device->SetWindowTitle = Emscripten_SetWindowTitle;
+    device->SetWindowIcon = Emscripten_SetWindowIcon;
+    device->SetWindowPosition = Emscripten_SetWindowPosition;*/
+    device->SetWindowSize = Emscripten_SetWindowSize;
+    /*device->ShowWindow = Emscripten_ShowWindow;
+    device->HideWindow = Emscripten_HideWindow;
+    device->RaiseWindow = Emscripten_RaiseWindow;
+    device->MaximizeWindow = Emscripten_MaximizeWindow;
+    device->MinimizeWindow = Emscripten_MinimizeWindow;
+    device->RestoreWindow = Emscripten_RestoreWindow;
+    device->SetWindowGrab = Emscripten_SetWindowGrab;*/
+    device->DestroyWindow = Emscripten_DestroyWindow;
+    device->SetWindowFullscreen = Emscripten_SetWindowFullscreen;
+
+    device->CreateWindowFramebuffer = Emscripten_CreateWindowFramebuffer;
+    device->UpdateWindowFramebuffer = Emscripten_UpdateWindowFramebuffer;
+    device->DestroyWindowFramebuffer = Emscripten_DestroyWindowFramebuffer;
+
+    device->GL_LoadLibrary = Emscripten_GLES_LoadLibrary;
+    device->GL_GetProcAddress = Emscripten_GLES_GetProcAddress;
+    device->GL_UnloadLibrary = Emscripten_GLES_UnloadLibrary;
+    device->GL_CreateContext = Emscripten_GLES_CreateContext;
+    device->GL_MakeCurrent = Emscripten_GLES_MakeCurrent;
+    device->GL_SetSwapInterval = Emscripten_GLES_SetSwapInterval;
+    device->GL_GetSwapInterval = Emscripten_GLES_GetSwapInterval;
+    device->GL_SwapWindow = Emscripten_GLES_SwapWindow;
+    device->GL_DeleteContext = Emscripten_GLES_DeleteContext;
+    device->GL_GetDrawableSize = Emscripten_GLES_GetDrawableSize;
+
+    device->free = Emscripten_DeleteDevice;
+
+    return device;
+}
+
+VideoBootStrap Emscripten_bootstrap = {
+    EMSCRIPTENVID_DRIVER_NAME, "SDL emscripten video driver",
+    Emscripten_Available, Emscripten_CreateDevice
+};
+
+
+int
+Emscripten_VideoInit(_THIS)
+{
+    SDL_DisplayMode mode;
+    double css_w, css_h;
+
+    /* Use a fake 32-bpp desktop mode */
+    mode.format = SDL_PIXELFORMAT_RGB888;
+
+    emscripten_get_element_css_size(NULL, &css_w, &css_h);
+
+    mode.w = css_w;
+    mode.h = css_h;
+
+    mode.refresh_rate = 0;
+    mode.driverdata = NULL;
+    if (SDL_AddBasicVideoDisplay(&mode) < 0) {
+        return -1;
+    }
+
+    SDL_zero(mode);
+    SDL_AddDisplayMode(&_this->displays[0], &mode);
+
+    Emscripten_InitMouse();
+
+    /* We're done! */
+    return 0;
+}
+
+static int
+Emscripten_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
+{
+    /* can't do this */
+    return 0;
+}
+
+static void
+Emscripten_VideoQuit(_THIS)
+{
+    Emscripten_FiniMouse();
+}
+
+static void
+Emscripten_PumpEvents(_THIS)
+{
+    /* do nothing. */
+}
+
+static int
+Emscripten_CreateWindow(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *wdata;
+    double scaled_w, scaled_h;
+    double css_w, css_h;
+
+    /* Allocate window internal data */
+    wdata = (SDL_WindowData *) SDL_calloc(1, sizeof(SDL_WindowData));
+    if (wdata == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
+        wdata->pixel_ratio = emscripten_get_device_pixel_ratio();
+    } else {
+        wdata->pixel_ratio = 1.0f;
+    }
+
+    scaled_w = SDL_floor(window->w * wdata->pixel_ratio);
+    scaled_h = SDL_floor(window->h * wdata->pixel_ratio);
+
+    emscripten_set_canvas_size(scaled_w, scaled_h);
+
+    emscripten_get_element_css_size(NULL, &css_w, &css_h);
+
+    wdata->external_size = css_w != scaled_w || css_h != scaled_h;
+
+    if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) {
+        /* external css has resized us */
+        scaled_w = css_w * wdata->pixel_ratio;
+        scaled_h = css_h * wdata->pixel_ratio;
+
+        emscripten_set_canvas_size(scaled_w, scaled_h);
+        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, css_w, css_h);
+    }
+
+    /* if the size is not being controlled by css, we need to scale down for hidpi */
+    if (!wdata->external_size) {
+        if (wdata->pixel_ratio != 1.0f) {
+            /*scale canvas down*/
+            emscripten_set_element_css_size(NULL, window->w, window->h);
+        }
+    }
+
+    wdata->windowed_width = scaled_w;
+    wdata->windowed_height = scaled_h;
+
+    if (window->flags & SDL_WINDOW_OPENGL) {
+        if (!_this->egl_data) {
+            if (SDL_GL_LoadLibrary(NULL) < 0) {
+                return -1;
+            }
+        }
+        wdata->egl_surface = SDL_EGL_CreateSurface(_this, NULL);
+
+        if (wdata->egl_surface == EGL_NO_SURFACE) {
+            return SDL_SetError("Could not create GLES window surface");
+        }
+    }
+
+    wdata->window = window;
+
+    /* Setup driver data for this window */
+    window->driverdata = wdata;
+
+    /* One window, it always has focus */
+    SDL_SetMouseFocus(window);
+    SDL_SetKeyboardFocus(window);
+
+    Emscripten_RegisterEventHandlers(wdata);
+
+    /* Window has been successfully created */
+    return 0;
+}
+
+static void Emscripten_SetWindowSize(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *data;
+
+    if (window->driverdata) {
+        data = (SDL_WindowData *) window->driverdata;
+        emscripten_set_canvas_size(window->w * data->pixel_ratio, window->h * data->pixel_ratio);
+
+        /*scale canvas down*/
+        if (!data->external_size && data->pixel_ratio != 1.0f) {
+            emscripten_set_element_css_size(NULL, window->w, window->h);
+        }
+    }
+}
+
+static void
+Emscripten_DestroyWindow(_THIS, SDL_Window * window)
+{
+    SDL_WindowData *data;
+
+    if(window->driverdata) {
+        data = (SDL_WindowData *) window->driverdata;
+
+        Emscripten_UnregisterEventHandlers(data);
+        if (data->egl_surface != EGL_NO_SURFACE) {
+            SDL_EGL_DestroySurface(_this, data->egl_surface);
+            data->egl_surface = EGL_NO_SURFACE;
+        }
+        SDL_free(window->driverdata);
+        window->driverdata = NULL;
+    }
+}
+
+static void
+Emscripten_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+{
+    SDL_WindowData *data;
+    if(window->driverdata) {
+        data = (SDL_WindowData *) window->driverdata;
+
+        if(fullscreen) {
+            data->requested_fullscreen_mode = window->flags & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
+            /*unset the fullscreen flags as we're not actually fullscreen yet*/
+            window->flags &= ~(SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN);
+
+            EM_ASM({
+                //reparent canvas (similar to Module.requestFullscreen)
+                var canvas = Module['canvas'];
+                if(canvas.parentNode.id != "SDLFullscreenElement") {
+                    var canvasContainer = document.createElement("div");
+                    canvasContainer.id = "SDLFullscreenElement";
+                    canvas.parentNode.insertBefore(canvasContainer, canvas);
+                    canvasContainer.appendChild(canvas);
+                }
+            });
+
+            int is_fullscreen;
+            emscripten_get_canvas_size(&data->windowed_width, &data->windowed_height, &is_fullscreen);
+            emscripten_request_fullscreen("SDLFullscreenElement", 1);
+        }
+        else
+            emscripten_exit_fullscreen();
+    }
+}
+
+#endif /* SDL_VIDEO_DRIVER_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/emscripten/SDL_emscriptenvideo.h	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,52 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef _SDL_emscriptenvideo_h
+#define _SDL_emscriptenvideo_h
+
+#include "../SDL_sysvideo.h"
+#include <emscripten/emscripten.h>
+#include <emscripten/html5.h>
+
+#include <EGL/egl.h>
+
+typedef struct SDL_WindowData
+{
+#if SDL_VIDEO_OPENGL_EGL
+    EGLSurface egl_surface;
+#endif
+    SDL_Window *window;
+    SDL_Surface *surface;
+
+    int windowed_width;
+    int windowed_height;
+
+    float pixel_ratio;
+
+    SDL_bool external_size;
+
+    int requested_fullscreen_mode;
+} SDL_WindowData;
+
+#endif /* _SDL_emscriptenvideo_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
--- a/test/Makefile.in	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/Makefile.in	Sat Dec 13 01:17:10 2014 -0500
@@ -38,6 +38,7 @@
 	testloadso$(EXE) \
 	testlock$(EXE) \
 	testmultiaudio$(EXE) \
+	testmultiaudio2$(EXE) \
 	testnative$(EXE) \
 	testoverlay2$(EXE) \
 	testplatform$(EXE) \
@@ -105,6 +106,9 @@
 testmultiaudio$(EXE): $(srcdir)/testmultiaudio.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
+testmultiaudio2$(EXE): $(srcdir)/testmultiaudio2.c
+	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+
 testatomic$(EXE): $(srcdir)/testatomic.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
@@ -196,6 +200,15 @@
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS) @XLIB@
 endif
 
+#there's probably a better way of doing this
+ifeq (@ISMACOSX@,false)
+ifeq (@ISWINDOWS@,false)
+ifeq (@ISUNIX@,false)
+testnative$(EXE): ;
+endif
+endif
+endif
+
 testoverlay2$(EXE): $(srcdir)/testoverlay2.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
--- a/test/checkkeys.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/checkkeys.c	Sat Dec 13 01:17:10 2014 -0500
@@ -19,8 +19,14 @@
 #include <stdlib.h>
 #include <string.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL.h"
 
+int done;
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -128,12 +134,37 @@
     SDL_Log("Text (%s): \"%s%s\"\n", expanded, *text == '"' ? "\\" : "", text);
 }
 
+void
+loop()
+{
+    SDL_Event event;
+    /* Check for events */
+    /*SDL_WaitEvent(&event); emscripten does not like waiting*/
+
+    while (SDL_PollEvent(&event)) {
+        switch (event.type) {
+        case SDL_KEYDOWN:
+        //case SDL_KEYUP:
+		    PrintKey(&event.key.keysym, (event.key.state == SDL_PRESSED) ? SDL_TRUE : SDL_FALSE, (event.key.repeat) ? SDL_TRUE : SDL_FALSE);
+            break;
+        case SDL_TEXTINPUT:
+            PrintText(event.text.text);
+            break;
+        case SDL_MOUSEBUTTONDOWN:
+            /* Any button press quits the app... */
+        case SDL_QUIT:
+            done = 1;
+            break;
+        default:
+            break;
+        }
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
     SDL_Window *window;
-    SDL_Event event;
-    int done;
 	
 	/* Enable standard application logging */
 	SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -163,26 +194,14 @@
 
     /* Watch keystrokes */
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
-        SDL_WaitEvent(&event);
-        switch (event.type) {
-        case SDL_KEYDOWN:
-        case SDL_KEYUP:
-			PrintKey(&event.key.keysym, (event.key.state == SDL_PRESSED) ? SDL_TRUE : SDL_FALSE, (event.key.repeat) ? SDL_TRUE : SDL_FALSE);
-            break;
-        case SDL_TEXTINPUT:
-            PrintText(event.text.text);
-            break;
-        case SDL_MOUSEBUTTONDOWN:
-            /* Any button press quits the app... */
-        case SDL_QUIT:
-            done = 1;
-            break;
-        default:
-            break;
-        }
+        loop();
     }
+#endif
 
     SDL_Quit();
     return (0);
--- a/test/configure	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/configure	Sat Dec 13 01:17:10 2014 -0500
@@ -2980,6 +2980,11 @@
         MATHLIB=""
         SYS_GL_LIBS="-lGLES_CM"
         ;;
+    *-*-emscripten* )
+                EXE=".bc"
+        MATHLIB=""
+        SYS_GL_LIBS=""
+        ;;
     *)
                 ISUNIX="true"
         EXE=""
--- a/test/configure.in	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/configure.in	Sat Dec 13 01:17:10 2014 -0500
@@ -65,6 +65,12 @@
         MATHLIB=""
         SYS_GL_LIBS="-lGLES_CM"
         ;;
+    *-*-emscripten* )
+        dnl This should really be .js, but we need to specify extra flags when compiling to js
+        EXE=".bc"
+        MATHLIB=""
+        SYS_GL_LIBS=""
+        ;;
     *)
         dnl Oh well, call it Unix...
         ISUNIX="true"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/emscripten/joystick-pre.js	Sat Dec 13 01:17:10 2014 -0500
@@ -0,0 +1,25 @@
+Module['arguments'] = ['0'];
+//Gamepads don't appear until a button is pressed and the joystick/gamepad tests expect one to be connected
+Module['preRun'].push(function()
+{
+    Module['print']("Waiting for gamepad...");
+    Module['addRunDependency']("gamepad");
+    window.addEventListener('gamepadconnected', function()
+    {
+        //OK, got one
+        Module['removeRunDependency']("gamepad");
+    }, false);
+
+    //chrome
+    if(!!navigator.webkitGetGamepads)
+    {
+        var timeout = function()
+        {
+            if(navigator.webkitGetGamepads()[0] !== undefined)
+                Module['removeRunDependency']("gamepad");
+            else
+                setTimeout(timeout, 100);
+        }
+        setTimeout(timeout, 100);
+    }
+});
--- a/test/loopwave.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/loopwave.c	Sat Dec 13 01:17:10 2014 -0500
@@ -24,6 +24,10 @@
 #include <signal.h>
 #endif
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL.h"
 #include "SDL_audio.h"
 
@@ -75,6 +79,13 @@
     done = 1;
 }
 
+void
+loop()
+{
+    if(done || (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING))
+        emscripten_cancel_main_loop();
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -131,8 +142,13 @@
 
     /* Let the audio run */
     SDL_PauseAudio(0);
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done && (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING))
         SDL_Delay(1000);
+#endif
 
     /* Clean up on signal */
     SDL_CloseAudio();
--- a/test/testautomation_surface.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testautomation_surface.c	Sat Dec 13 01:17:10 2014 -0500
@@ -8,6 +8,7 @@
 #define _CRT_NONSTDC_NO_DEPRECATE
 
 #include <stdio.h>
+#include <unistd.h>
 #include <sys/stat.h>
 
 #include "SDL.h"
--- a/test/testdraw2.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testdraw2.c	Sat Dec 13 01:17:10 2014 -0500
@@ -16,6 +16,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 #define NUM_OBJECTS 100
@@ -29,6 +33,8 @@
 static int current_color = 255;
 static SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
 
+int done;
+
 void
 DrawPoints(SDL_Renderer * renderer)
 {
@@ -169,11 +175,35 @@
     }
 }
 
+void
+loop()
+{
+    int i;
+    SDL_Event event;
+
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        SDL_Renderer *renderer = state->renderers[i];
+        if (state->windows[i] == NULL)
+            continue;
+        SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
+        SDL_RenderClear(renderer);
+
+        DrawRects(renderer);
+        DrawLines(renderer);
+        DrawPoints(renderer);
+
+        SDL_RenderPresent(renderer);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    int i, done;
-    SDL_Event event;
+    int i;
     Uint32 then, now, frames;
 
 	/* Enable standard application logging */
@@ -245,26 +275,16 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
+        loop();
         }
-        for (i = 0; i < state->num_windows; ++i) {
-            SDL_Renderer *renderer = state->renderers[i];
-            if (state->windows[i] == NULL)
-                continue;
-            SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
-            SDL_RenderClear(renderer);
+#endif
 
-            DrawRects(renderer);
-            DrawLines(renderer);
-            DrawPoints(renderer);
-
-            SDL_RenderPresent(renderer);
-        }
-    }
 
     SDLTest_CommonQuit(state);
 
--- a/test/testdrawchessboard.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testdrawchessboard.c	Sat Dec 13 01:17:10 2014 -0500
@@ -14,8 +14,19 @@
 
 /* Sample program:  Draw a Chess Board  by using SDL_CreateSoftwareRenderer API */
 
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL.h"
 
+SDL_Window *window;
+SDL_Renderer *renderer;
+int done;
+
 void
 DrawChessBoard(SDL_Renderer * renderer)
 {
@@ -44,12 +55,33 @@
 	}
 }
 
+void
+loop()
+{
+    SDL_Event e;
+	if (SDL_PollEvent(&e)) {
+		if (e.type == SDL_QUIT) {
+			done = 1;
+			return;
+		}
+
+		if(e.key.keysym.sym == SDLK_ESCAPE) {
+			done = 1;
+			return;
+		}
+	}
+	
+	DrawChessBoard(renderer);
+	
+	/* Got everything on rendering surface,
+	   now Update the drawing image on window screen */
+	SDL_UpdateWindowSurface(window);
+}
+
 int
 main(int argc, char *argv[])
 {
-	SDL_Window *window;
 	SDL_Surface *surface;
-	SDL_Renderer *renderer;
 
     /* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -83,24 +115,14 @@
 
 
 	/* Draw the Image on rendering surface */
-	while(1)
-	{
-		SDL_Event e;
-		if (SDL_PollEvent(&e)) {
-			if (e.type == SDL_QUIT) 
-				break;
-
-			if(e.key.keysym.sym == SDLK_ESCAPE)
-				break;
-		}
-		
-		DrawChessBoard(renderer);
-		
-		/* Got everything on rendering surface,
- 		   now Update the drawing image on window screen */
-		SDL_UpdateWindowSurface(window);
-
+	done = 0;
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
+    while (!done) {
+        loop();
 	}
+#endif
 
 	return 0;
 }
--- a/test/testfilesystem.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testfilesystem.c	Sat Dec 13 01:17:10 2014 -0500
@@ -25,6 +25,25 @@
         return 1;
     }
 
+    char *base_path = SDL_GetBasePath();
+    if(base_path == NULL){
+      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't find base path: %s\n",
+                   SDL_GetError());
+      return 0;
+    }
+
+    SDL_Log("base path: '%s'\n", SDL_GetBasePath());
+    SDL_free(base_path);
+
+    char *pref_path = SDL_GetPrefPath("libsdl", "testfilesystem");
+    if(pref_path == NULL){
+      SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't find pref path: %s\n",
+                   SDL_GetError());
+      return 0;
+    }
+    SDL_Log("pref path: '%s'\n", SDL_GetPrefPath("libsdl", "testfilesystem"));
+    SDL_free(pref_path);
+
     SDL_Log("base path: '%s'\n", SDL_GetBasePath());
     SDL_Log("pref path: '%s'\n", SDL_GetPrefPath("libsdl", "testfilesystem"));
 
--- a/test/testgamecontroller.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testgamecontroller.c	Sat Dec 13 01:17:10 2014 -0500
@@ -18,6 +18,10 @@
 
 #include "SDL.h"
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #ifndef SDL_JOYSTICK_DISABLED
 
 #ifdef __IPHONEOS__
@@ -28,6 +32,40 @@
 #define SCREEN_HEIGHT   317
 #endif
 
+/* This is indexed by SDL_GameControllerButton. */
+static const struct { int x; int y; } button_positions[] = {
+    {387, 167},  /* A */
+    {431, 132},  /* B */
+    {342, 132},  /* X */
+    {389, 101},  /* Y */
+    {174, 132},  /* BACK */
+    {233, 132},  /* GUIDE */
+    {289, 132},  /* START */
+    {75,  154},  /* LEFTSTICK */
+    {305, 230},  /* RIGHTSTICK */
+    {77,  40},   /* LEFTSHOULDER */
+    {396, 36},   /* RIGHTSHOULDER */
+    {154, 188},  /* DPAD_UP */
+    {154, 249},  /* DPAD_DOWN */
+    {116, 217},  /* DPAD_LEFT */
+    {186, 217},  /* DPAD_RIGHT */
+};
+
+/* This is indexed by SDL_GameControllerAxis. */
+static const struct { int x; int y; double angle; } axis_positions[] = {
+    {75,  154, 0.0},  /* LEFTX */
+    {75,  154, 90.0},  /* LEFTY */
+    {305, 230, 0.0},  /* RIGHTX */
+    {305, 230, 90.0},  /* RIGHTY */
+    {91, 0, 90.0},     /* TRIGGERLEFT */
+    {375, 0, 90.0},    /* TRIGGERRIGHT */
+};
+
+SDL_Renderer *screen = NULL;
+SDL_bool retval = SDL_FALSE;
+SDL_bool done = SDL_FALSE;
+SDL_Texture *background, *button, *axis;
+
 static SDL_Texture *
 LoadTexture(SDL_Renderer *renderer, char *file, SDL_bool transparent)
 {
@@ -60,50 +98,71 @@
     return texture;
 }
 
+void
+loop(void *arg)
+{
+    SDL_Event event;
+    int i;
+    SDL_GameController *gamecontroller = (SDL_GameController *)arg;
+
+    /* blank screen, set up for drawing this frame. */
+    SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
+    SDL_RenderClear(screen);
+    SDL_RenderCopy(screen, background, NULL, NULL);
+
+    while (SDL_PollEvent(&event)) {
+        switch (event.type) {
+        case SDL_KEYDOWN:
+            if (event.key.keysym.sym != SDLK_ESCAPE) {
+                break;
+            }
+            /* Fall through to signal quit */
+        case SDL_QUIT:
+            done = SDL_TRUE;
+            break;
+        default:
+            break;
+        }
+    }
+
+    /* Update visual controller state */
+    for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
+        if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
+            const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
+            SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, 0);
+        }
+    }
+
+    for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
+        const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
+        const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
+        if (value < -deadzone) {
+            const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
+            const double angle = axis_positions[i].angle;
+            SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, 0);
+        } else if (value > deadzone) {
+            const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
+            const double angle = axis_positions[i].angle + 180.0;
+            SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, 0);
+        }
+    }
+
+    SDL_RenderPresent(screen);
+
+    if (!SDL_GameControllerGetAttached(gamecontroller)) {
+        done = SDL_TRUE;
+        retval = SDL_TRUE;  /* keep going, wait for reattach. */
+    }
+}
+
 SDL_bool
 WatchGameController(SDL_GameController * gamecontroller)
 {
-    /* This is indexed by SDL_GameControllerButton. */
-    static const struct { int x; int y; } button_positions[] = {
-        {387, 167},  /* A */
-        {431, 132},  /* B */
-        {342, 132},  /* X */
-        {389, 101},  /* Y */
-        {174, 132},  /* BACK */
-        {233, 132},  /* GUIDE */
-        {289, 132},  /* START */
-        {75,  154},  /* LEFTSTICK */
-        {305, 230},  /* RIGHTSTICK */
-        {77,  40},   /* LEFTSHOULDER */
-        {396, 36},   /* RIGHTSHOULDER */
-        {154, 188},  /* DPAD_UP */
-        {154, 249},  /* DPAD_DOWN */
-        {116, 217},  /* DPAD_LEFT */
-        {186, 217},  /* DPAD_RIGHT */
-    };
-
-    /* This is indexed by SDL_GameControllerAxis. */
-    static const struct { int x; int y; double angle; } axis_positions[] = {
-        {75,  154, 0.0},  /* LEFTX */
-        {75,  154, 90.0},  /* LEFTY */
-        {305, 230, 0.0},  /* RIGHTX */
-        {305, 230, 90.0},  /* RIGHTY */
-        {91, 0, 90.0},     /* TRIGGERLEFT */
-        {375, 0, 90.0},    /* TRIGGERRIGHT */
-    };
-
     const char *name = SDL_GameControllerName(gamecontroller);
     const char *basetitle = "Game Controller Test: ";
     const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1;
     char *title = (char *)SDL_malloc(titlelen);
-    SDL_Texture *background, *button, *axis;
     SDL_Window *window = NULL;
-    SDL_Renderer *screen = NULL;
-    SDL_bool retval = SDL_FALSE;
-    SDL_bool done = SDL_FALSE;
-    SDL_Event event;
-    int i;
-
     if (title) {
         SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
     }
@@ -151,56 +210,13 @@
     SDL_Log("Watching controller %s\n",  name ? name : "Unknown Controller");
 
     /* Loop, getting controller events! */
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop_arg(loop, gamecontroller, 0, 1);
+#else
     while (!done) {
-        /* blank screen, set up for drawing this frame. */
-        SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
-        SDL_RenderClear(screen);
-        SDL_RenderCopy(screen, background, NULL, NULL);
-
-        while (SDL_PollEvent(&event)) {
-            switch (event.type) {
-            case SDL_KEYDOWN:
-                if (event.key.keysym.sym != SDLK_ESCAPE) {
-                    break;
-                }
-                /* Fall through to signal quit */
-            case SDL_QUIT:
-                done = SDL_TRUE;
-                break;
-            default:
-                break;
-            }
-        }
-
-        /* Update visual controller state */
-        for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
-            if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
-                const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
-                SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, 0);
-            }
-        }
-
-        for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
-            const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
-            const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
-            if (value < -deadzone) {
-                const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
-                const double angle = axis_positions[i].angle;
-                SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, 0);
-            } else if (value > deadzone) {
-                const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
-                const double angle = axis_positions[i].angle + 180.0;
-                SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, 0);
-            }
-        }
-
-        SDL_RenderPresent(screen);
-
-        if (!SDL_GameControllerGetAttached(gamecontroller)) {
-            done = SDL_TRUE;
-            retval = SDL_TRUE;  /* keep going, wait for reattach. */
-        }
+        loop(gamecontroler);
     }
+#endif
 
     SDL_DestroyRenderer(screen);
     SDL_DestroyWindow(window);
--- a/test/testgesture.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testgesture.c	Sat Dec 13 01:17:10 2014 -0500
@@ -17,6 +17,10 @@
 
 #include "SDL.h"
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #define WIDTH 640
 #define HEIGHT 480
 #define BPP 4
@@ -34,6 +38,9 @@
 
 static int colors[7] = {0xFF,0xFF00,0xFF0000,0xFFFF00,0x00FFFF,0xFF00FF,0xFFFFFF};
 
+SDL_Surface *screen;
+SDL_bool quitting = SDL_FALSE;
+
 typedef struct {
   float x,y;
 } Point;
@@ -161,33 +168,13 @@
   return window;
 }
 
-int main(int argc, char* argv[])
+void loop()
 {
-  SDL_Window *window = NULL;
-  SDL_Surface *screen;
-  SDL_Event event;
-  SDL_bool quitting = SDL_FALSE;
-  SDL_RWops *stream;
-
-  /* Enable standard application logging */
-  SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
+    SDL_Event event;
+    SDL_RWops *stream;
 
-  /* gesture variables */
-  knob.r = .1f;
-  knob.ang = 0;
-
-  if (SDL_Init(SDL_INIT_VIDEO) < 0 ) return 1;
-
-  if (!(window = initWindow(window, WIDTH, HEIGHT)) ||
-      !(screen = SDL_GetWindowSurface(window)))
+    while(SDL_PollEvent(&event))
     {
-      SDL_Quit();
-      return 1;
-    }
-
-  while(!quitting) {
-    while(SDL_PollEvent(&event))
-      {
     /* Record _all_ events */
     events[eventWrite & (EVENT_BUF_SIZE-1)] = event;
     eventWrite++;
@@ -233,7 +220,7 @@
               !(screen = SDL_GetWindowSurface(window)))
           {
         SDL_Quit();
-        return 1;
+        exit(1);
           }
             }
         break;
@@ -278,9 +265,36 @@
         SDL_Log("Recorded gesture: %"SDL_PRIs64"",event.dgesture.gestureId);
         break;
       }
-      }
-    DrawScreen(screen, window);
+    }
+    DrawScreen(screen);
+}
+
+int main(int argc, char* argv[])
+{
+
+  /* Enable standard application logging */
+  SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
+
+  /* gesture variables */
+  knob.r = .1f;
+  knob.ang = 0;
+
+  if (SDL_Init(SDL_INIT_VIDEO) < 0 ) return 1;
+
+  if (!(screen = initScreen(WIDTH,HEIGHT)))
+  {
+      SDL_Quit();
+      return 1;
   }
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
+    while(!quitting) {
+        loop();
+    }
+#endif
+
   SDL_Quit();
   return 0;
 }
--- a/test/testgles2.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testgles2.c	Sat Dec 13 01:17:10 2014 -0500
@@ -14,6 +14,10 @@
 #include <string.h>
 #include <math.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 #if defined(__IPHONEOS__) || defined(__ANDROID__) || defined(__NACL__)
@@ -408,17 +412,72 @@
     GL_CHECK(ctx.glDrawArrays(GL_TRIANGLES, 0, 36));
 }
 
+int done;
+Uint32 frames;
+shader_data *datas;
+
+void loop()
+{
+    SDL_Event event;
+    int i;
+    int status;
+
+    /* Check for events */
+    ++frames;
+    while (SDL_PollEvent(&event) && !done) {
+        switch (event.type) {
+        case SDL_WINDOWEVENT:
+            switch (event.window.event) {
+                case SDL_WINDOWEVENT_RESIZED:
+                    for (i = 0; i < state->num_windows; ++i) {
+                        if (event.window.windowID == SDL_GetWindowID(state->windows[i])) {
+                            int w, h;
+                            status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
+                            if (status) {
+                                SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
+                                break;
+                            }
+                            /* Change view port to the new window dimensions */
+                            SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
+                            ctx.glViewport(0, 0, w, h);
+                            state->window_w = event.window.data1;
+                            state->window_h = event.window.data2;
+                            /* Update window content */
+                            Render(event.window.data1, event.window.data2, &datas[i]);
+                            SDL_GL_SwapWindow(state->windows[i]);
+                            break;
+                        }
+                    }
+                    break;
+            }
+        }
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+    if (!done) {
+      for (i = 0; i < state->num_windows; ++i) {
+          status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
+          if (status) {
+              SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
+
+              /* Continue for next window */
+              continue;
+          }
+          Render(state->window_w, state->window_h, &datas[i]);
+          SDL_GL_SwapWindow(state->windows[i]);
+      }
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
     int fsaa, accel;
     int value;
-    int i, done;
+    int i;
     SDL_DisplayMode mode;
-    SDL_Event event;
-    Uint32 then, now, frames;
+    Uint32 then, now;
     int status;
-    shader_data *datas, *data;
+    shader_data *data;
 
     /* Initialize parameters */
     fsaa = 0;
@@ -581,6 +640,7 @@
     /* Set rendering settings for each context */
     for (i = 0; i < state->num_windows; ++i) {
 
+        int w, h;
         status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
         if (status) {
             SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
@@ -588,7 +648,8 @@
             /* Continue for next window */
             continue;
         }
-        ctx.glViewport(0, 0, state->window_w, state->window_h);
+        SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
+        ctx.glViewport(0, 0, w, h);
 
         data = &datas[i];
         data->angle_x = 0; data->angle_y = 0; data->angle_z = 0;
@@ -630,48 +691,14 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
-        ++frames;
-        while (SDL_PollEvent(&event) && !done) {
-            switch (event.type) {
-            case SDL_WINDOWEVENT:
-                switch (event.window.event) {
-                    case SDL_WINDOWEVENT_RESIZED:
-                        for (i = 0; i < state->num_windows; ++i) {
-                            if (event.window.windowID == SDL_GetWindowID(state->windows[i])) {
-                                status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
-                                if (status) {
-                                    SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
-                                    break;
-                                }
-                                /* Change view port to the new window dimensions */
-                                ctx.glViewport(0, 0, event.window.data1, event.window.data2);
-                                /* Update window content */
-                                Render(event.window.data1, event.window.data2, &datas[i]);
-                                SDL_GL_SwapWindow(state->windows[i]);
-                                break;
-                            }
-                        }
-                        break;
-                }
-            }
-            SDLTest_CommonEvent(state, &event, &done);
-        }
-        if (!done) {
-          for (i = 0; i < state->num_windows; ++i) {
-              status = SDL_GL_MakeCurrent(state->windows[i], context[i]);
-              if (status) {
-                  SDL_Log("SDL_GL_MakeCurrent(): %s\n", SDL_GetError());
-
-                  /* Continue for next window */
-                  continue;
-              }
-              Render(state->window_w, state->window_h, &datas[i]);
-              SDL_GL_SwapWindow(state->windows[i]);
-          }
-        }
+        loop();
     }
+#endif
 
     /* Print out some timing information */
     now = SDL_GetTicks();
--- a/test/testintersections.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testintersections.c	Sat Dec 13 01:17:10 2014 -0500
@@ -16,6 +16,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 #define SWAP(typ,a,b) do{typ t=a;a=b;b=t;}while(0)
@@ -30,6 +34,9 @@
 static int current_color = 255;
 static SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
 
+int mouse_begin_x = -1, mouse_begin_y = -1;
+int done;
+
 void
 DrawPoints(SDL_Renderer * renderer)
 {
@@ -191,12 +198,71 @@
         }
 }
 
+void
+loop()
+{
+    int i;
+    SDL_Event event;
+
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+        switch (event.type) {
+        case SDL_MOUSEBUTTONDOWN:
+            mouse_begin_x = event.button.x;
+            mouse_begin_y = event.button.y;
+            break;
+        case SDL_MOUSEBUTTONUP:
+            if (event.button.button == 3)
+                add_line(mouse_begin_x, mouse_begin_y, event.button.x,
+                         event.button.y);
+            if (event.button.button == 1)
+                add_rect(mouse_begin_x, mouse_begin_y, event.button.x,
+                         event.button.y);
+            break;
+        case SDL_KEYDOWN:
+            switch (event.key.keysym.sym) {
+            case 'l':
+                if (event.key.keysym.mod & KMOD_SHIFT)
+                    num_lines = 0;
+                else
+                    add_line(rand() % 640, rand() % 480, rand() % 640,
+                             rand() % 480);
+                break;
+            case 'r':
+                if (event.key.keysym.mod & KMOD_SHIFT)
+                    num_rects = 0;
+                else
+                    add_rect(rand() % 640, rand() % 480, rand() % 640,
+                             rand() % 480);
+                break;
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        SDL_Renderer *renderer = state->renderers[i];
+        if (state->windows[i] == NULL)
+            continue;
+        SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
+        SDL_RenderClear(renderer);
+
+        DrawRects(renderer);
+        DrawPoints(renderer);
+        DrawRectRectIntersections(renderer);
+        DrawLines(renderer);
+        DrawRectLineIntersections(renderer);
+
+        SDL_RenderPresent(renderer);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    int mouse_begin_x = -1, mouse_begin_y = -1;
-    int i, done;
-    SDL_Event event;
+    int i;
     Uint32 then, now, frames;
 
     /* Enable standard application logging */
@@ -268,62 +334,15 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
-            switch (event.type) {
-            case SDL_MOUSEBUTTONDOWN:
-                mouse_begin_x = event.button.x;
-                mouse_begin_y = event.button.y;
-                break;
-            case SDL_MOUSEBUTTONUP:
-                if (event.button.button == 3)
-                    add_line(mouse_begin_x, mouse_begin_y, event.button.x,
-                             event.button.y);
-                if (event.button.button == 1)
-                    add_rect(mouse_begin_x, mouse_begin_y, event.button.x,
-                             event.button.y);
-                break;
-            case SDL_KEYDOWN:
-                switch (event.key.keysym.sym) {
-                case 'l':
-                    if (event.key.keysym.mod & KMOD_SHIFT)
-                        num_lines = 0;
-                    else
-                        add_line(rand() % 640, rand() % 480, rand() % 640,
-                                 rand() % 480);
-                    break;
-                case 'r':
-                    if (event.key.keysym.mod & KMOD_SHIFT)
-                        num_rects = 0;
-                    else
-                        add_rect(rand() % 640, rand() % 480, rand() % 640,
-                                 rand() % 480);
-                    break;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        for (i = 0; i < state->num_windows; ++i) {
-            SDL_Renderer *renderer = state->renderers[i];
-            if (state->windows[i] == NULL)
-                continue;
-            SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
-            SDL_RenderClear(renderer);
-
-            DrawRects(renderer);
-            DrawPoints(renderer);
-            DrawRectRectIntersections(renderer);
-            DrawLines(renderer);
-            DrawRectLineIntersections(renderer);
-
-            SDL_RenderPresent(renderer);
-        }
+        loop();
     }
+#endif
 
     SDLTest_CommonQuit(state);
 
--- a/test/testjoystick.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testjoystick.c	Sat Dec 13 01:17:10 2014 -0500
@@ -18,6 +18,10 @@
 
 #include "SDL.h"
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #ifndef SDL_JOYSTICK_DISABLED
 
 #ifdef __IPHONEOS__
@@ -28,6 +32,9 @@
 #define SCREEN_HEIGHT   480
 #endif
 
+SDL_Renderer *screen = NULL;
+SDL_bool retval = SDL_FALSE;
+SDL_bool done = SDL_FALSE;
 
 static void
 DrawRect(SDL_Renderer *r, const int x, const int y, const int w, const int h)
@@ -36,50 +43,15 @@
     SDL_RenderFillRect(r, &area);
 }
 
-static SDL_bool
-WatchJoystick(SDL_Joystick * joystick)
+void
+loop(void *arg)
 {
-    SDL_Window *window = NULL;
-    SDL_Renderer *screen = NULL;
-    const char *name = NULL;
-    SDL_bool retval = SDL_FALSE;
-    SDL_bool done = SDL_FALSE;
     SDL_Event event;
     int i;
-
-    /* Create a window to display joystick axis position */
-    window = SDL_CreateWindow("Joystick Test", SDL_WINDOWPOS_CENTERED,
-                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
-                              SCREEN_HEIGHT, 0);
-    if (window == NULL) {
-        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
-        return SDL_FALSE;
-    }
-
-    screen = SDL_CreateRenderer(window, -1, 0);
-    if (screen == NULL) {
-        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
-        SDL_DestroyWindow(window);
-        return SDL_FALSE;
-    }
+    SDL_Joystick *joystick = (SDL_Joystick *)arg;
 
-    SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
-    SDL_RenderClear(screen);
-    SDL_RenderPresent(screen);
-    SDL_RaiseWindow(window);
-
-    /* Print info about the joystick we are watching */
-    name = SDL_JoystickName(joystick);
-    SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
-           name ? name : "Unknown Joystick");
-    SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
-           SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
-           SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
-
-    /* Loop, getting joystick events! */
-    while (!done) {
         /* blank screen, set up for drawing this frame. */
-        SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
+    SDL_SetRenderDrawColor(screen, 0x0, 0x0, 0x0, SDL_ALPHA_OPAQUE);
         SDL_RenderClear(screen);
 
         while (SDL_PollEvent(&event)) {
@@ -197,8 +169,53 @@
             done = SDL_TRUE;
             retval = SDL_TRUE;  /* keep going, wait for reattach. */
         }
+}
+
+static SDL_bool
+WatchJoystick(SDL_Joystick * joystick)
+{
+    SDL_Window *window = NULL;
+    const char *name = NULL;
+
+
+    /* Create a window to display joystick axis position */
+    window = SDL_CreateWindow("Joystick Test", SDL_WINDOWPOS_CENTERED,
+                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
+                              SCREEN_HEIGHT, 0);
+    if (window == NULL) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
+        return SDL_FALSE;
     }
 
+    screen = SDL_CreateRenderer(window, -1, 0);
+    if (screen == NULL) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
+        SDL_DestroyWindow(window);
+        return SDL_FALSE;
+    }
+
+    SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
+    SDL_RenderClear(screen);
+    SDL_RenderPresent(screen);
+    SDL_RaiseWindow(window);
+
+    /* Print info about the joystick we are watching */
+    name = SDL_JoystickName(joystick);
+    SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
+           name ? name : "Unknown Joystick");
+    SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
+           SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
+           SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
+
+    /* Loop, getting joystick events! */
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop_arg(loop, joystick, 0, 1);
+#else
+    while (!done) {
+        loop(joystick);
+    }
+#endif
+
     SDL_DestroyRenderer(screen);
     SDL_DestroyWindow(window);
     return retval;
--- a/test/testmultiaudio.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testmultiaudio.c	Sat Dec 13 01:17:10 2014 -0500
@@ -13,6 +13,10 @@
 
 #include <stdio.h> /* for fflush() and stdout */
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 static SDL_AudioSpec spec;
 static Uint8 *sound = NULL;     /* Pointer to wave data */
 static Uint32 soundlen = 0;     /* Length of wave data */
@@ -24,6 +28,8 @@
     volatile int done;
 } callback_data;
 
+callback_data cbd[64];
+
 void SDLCALL
 play_through_once(void *arg, Uint8 * stream, int len)
 {
@@ -44,10 +50,21 @@
     }
 }
 
+void
+loop()
+{
+    if(cbd[0].done) {
+        emscripten_cancel_main_loop();
+        SDL_PauseAudioDevice(cbd[0].dev, 1);
+        SDL_CloseAudioDevice(cbd[0].dev);
+        SDL_FreeWAV(sound);
+        SDL_Quit();
+    }
+}
+
 static void
 test_multi_audio(int devcount)
 {
-    callback_data cbd[64];
     int keep_going = 1;
     int i;
     
@@ -78,14 +95,19 @@
             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Open device failed: %s\n", SDL_GetError());
         } else {
             SDL_PauseAudioDevice(cbd[0].dev, 0);
-            while (!cbd[0].done) {
-#ifdef __ANDROID__                
+#ifdef __EMSCRIPTEN__
+            emscripten_set_main_loop(loop, 0, 1);
+#else
+            while (!cbd[0].done)
+            {
+                #ifdef __ANDROID__                
                 /* Empty queue, some application events would prevent pause. */
                 while (SDL_PollEvent(&event)){}
-#endif                
+                #endif                
                 SDL_Delay(100);
             }
             SDL_PauseAudioDevice(cbd[0].dev, 1);
+#endif
             SDL_Log("done.\n");
             SDL_CloseAudioDevice(cbd[0].dev);
         }
@@ -116,21 +138,13 @@
                 keep_going = 1;
             }
         }
-#ifdef __ANDROID__        
+        #ifdef __ANDROID__        
         /* Empty queue, some application events would prevent pause. */
         while (SDL_PollEvent(&event)){}
-#endif        
+        #endif        
+
         SDL_Delay(100);
     }
-
-    for (i = 0; i < devcount; i++) {
-        if (cbd[i].dev) {
-            SDL_PauseAudioDevice(cbd[i].dev, 1);
-            SDL_CloseAudioDevice(cbd[i].dev);
-        }
-    }
-
-    SDL_Log("All done!\n");
 }
 
 
--- a/test/testoverlay2.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testoverlay2.c	Sat Dec 13 01:17:10 2014 -0500
@@ -20,6 +20,10 @@
 #include <stdio.h>
 #include <string.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL.h"
 
 #define MOOSEPIC_W 64
@@ -135,6 +139,18 @@
     , {239, 206, 173}
 };
 
+Uint8 MooseFrame[MOOSEFRAMES_COUNT][MOOSEFRAME_SIZE*2];
+SDL_Texture *MooseTexture;
+SDL_Rect displayrect;
+int window_w;
+int window_h;
+SDL_Window *window;
+SDL_Renderer *renderer;
+int paused = 0;
+int i;
+SDL_bool done = SDL_FALSE;
+Uint32 pixel_format = SDL_PIXELFORMAT_YV12;
+int fpsdelay;
 
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
@@ -246,21 +262,65 @@
     SDL_Log("\n");
 }
 
+void
+loop()
+{
+    SDL_Event event;
+
+    while (SDL_PollEvent(&event)) {
+        switch (event.type) {
+        case SDL_WINDOWEVENT:
+            if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
+                SDL_RenderSetViewport(renderer, NULL);
+                displayrect.w = window_w = event.window.data1;
+                displayrect.h = window_h = event.window.data2;
+            }
+            break;
+        case SDL_MOUSEBUTTONDOWN:
+            displayrect.x = event.button.x - window_w / 2;
+            displayrect.y = event.button.y - window_h / 2;
+            break;
+        case SDL_MOUSEMOTION:
+            if (event.motion.state) {
+                displayrect.x = event.motion.x - window_w / 2;
+                displayrect.y = event.motion.y - window_h / 2;
+            }
+            break;
+        case SDL_KEYDOWN:
+            if (event.key.keysym.sym == SDLK_SPACE) {
+                paused = !paused;
+                break;
+            }
+            if (event.key.keysym.sym != SDLK_ESCAPE) {
+                break;
+            }
+        case SDL_QUIT:
+            done = SDL_TRUE;
+            break;
+        }
+    }
+
+#ifndef __EMSCRIPTEN__
+    SDL_Delay(fpsdelay);
+#endif
+
+    if (!paused) {
+        i = (i + 1) % MOOSEFRAMES_COUNT;
+
+        SDL_UpdateTexture(MooseTexture, NULL, MooseFrame[i], MOOSEPIC_W*SDL_BYTESPERPIXEL(pixel_format));
+    }
+    SDL_RenderClear(renderer);
+    SDL_RenderCopy(renderer, MooseTexture, NULL, &displayrect);
+    SDL_RenderPresent(renderer);
+}
+
 int
 main(int argc, char **argv)
 {
     Uint8 *RawMooseData;
     SDL_RWops *handle;
-    int window_w;
-    int window_h;
     SDL_Window *window;
-    SDL_Renderer *renderer;
-    Uint8 MooseFrame[MOOSEFRAMES_COUNT][MOOSEFRAME_SIZE*2];
-    SDL_Texture *MooseTexture;
-    SDL_Rect displayrect;
-    SDL_Event event;
-    int paused = 0;
-    int i, j;
+    int j;
     int fps = 12;
     int fpsdelay;
     int nodelay = 0;
@@ -270,7 +330,6 @@
     Uint32 pixel_format = SDL_PIXELFORMAT_YV12;
 #endif
     int scale = 5;
-    SDL_bool done = SDL_FALSE;
 
     /* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -430,50 +489,14 @@
     SDL_EventState(SDL_KEYUP, SDL_IGNORE);
 
     /* Loop, waiting for QUIT or RESIZE */
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, nodelay ? 0 : fps, 1);
+#else
     while (!done) {
-        while (SDL_PollEvent(&event)) {
-            switch (event.type) {
-            case SDL_WINDOWEVENT:
-                if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
-                    SDL_RenderSetViewport(renderer, NULL);
-                    displayrect.w = window_w = event.window.data1;
-                    displayrect.h = window_h = event.window.data2;
-                }
-                break;
-            case SDL_MOUSEBUTTONDOWN:
-                displayrect.x = event.button.x - window_w / 2;
-                displayrect.y = event.button.y - window_h / 2;
-                break;
-            case SDL_MOUSEMOTION:
-                if (event.motion.state) {
-                    displayrect.x = event.motion.x - window_w / 2;
-                    displayrect.y = event.motion.y - window_h / 2;
-                }
-                break;
-            case SDL_KEYDOWN:
-                if (event.key.keysym.sym == SDLK_SPACE) {
-                    paused = !paused;
-                    break;
-                }
-                if (event.key.keysym.sym != SDLK_ESCAPE) {
-                    break;
-                }
-            case SDL_QUIT:
-                done = SDL_TRUE;
-                break;
+        loop();
             }
-        }
-        SDL_Delay(fpsdelay);
-
-        if (!paused) {
-            i = (i + 1) % MOOSEFRAMES_COUNT;
+#endif
 
-            SDL_UpdateTexture(MooseTexture, NULL, MooseFrame[i], MOOSEPIC_W*SDL_BYTESPERPIXEL(pixel_format));
-        }
-        SDL_RenderClear(renderer);
-        SDL_RenderCopy(renderer, MooseTexture, NULL, &displayrect);
-        SDL_RenderPresent(renderer);
-    }
     SDL_DestroyRenderer(renderer);
     quit(0);
     return 0;
--- a/test/testrelative.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testrelative.c	Sat Dec 13 01:17:10 2014 -0500
@@ -18,8 +18,14 @@
 
 #include "SDL_test_common.h"
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
 
 static SDLTest_CommonState *state;
+int i, done;
+SDL_Rect rect;
+SDL_Event event;
 
 static void
 DrawRects(SDL_Renderer * renderer, SDL_Rect * rect)
@@ -28,12 +34,36 @@
     SDL_RenderFillRect(renderer, rect);
 }
 
+static void
+loop(){
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+        switch(event.type) {
+        case SDL_MOUSEMOTION:
+            {
+                rect.x += event.motion.xrel;
+                rect.y += event.motion.yrel;
+            }
+            break;
+        }
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        SDL_Renderer *renderer = state->renderers[i];
+        if (state->windows[i] == NULL)
+            continue;
+        SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
+        SDL_RenderClear(renderer);
+
+        DrawRects(renderer, &rect);
+
+        SDL_RenderPresent(renderer);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    int i, done;
-    SDL_Rect rect;
-    SDL_Event event;
 
     /* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -69,32 +99,13 @@
     rect.h = 10;
     /* Main render loop */
     done = 0;
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
-            switch(event.type) {
-                case SDL_MOUSEMOTION:
-                {
-                    rect.x += event.motion.xrel;
-                    rect.y += event.motion.yrel;
-                }
-                break;
-            }
+        loop();
         }
-        for (i = 0; i < state->num_windows; ++i) {
-            SDL_Renderer *renderer = state->renderers[i];
-            if (state->windows[i] == NULL)
-                continue;
-            SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
-            SDL_RenderClear(renderer);
-
-            DrawRects(renderer, &rect);
-
-            SDL_RenderPresent(renderer);
-        }
-    }
-
+#endif
     SDLTest_CommonQuit(state);
     return 0;
 }
--- a/test/testrendercopyex.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testrendercopyex.c	Sat Dec 13 01:17:10 2014 -0500
@@ -15,6 +15,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 
@@ -29,6 +33,9 @@
     int scale_direction;
 } DrawState;
 
+DrawState *drawstates;
+int done;
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -130,12 +137,27 @@
     /* SDL_Delay(10); */
 }
 
+void loop()
+{
+    int i;
+    SDL_Event event;
+
+    /* Check for events */
+
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        if (state->windows[i] == NULL)
+            continue;
+        Draw(&drawstates[i]);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    DrawState *drawstates;
-    int i, done;
-    SDL_Event event;
+    int i;
     int frames;
     Uint32 then, now;
 
@@ -181,19 +203,15 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
+        loop();
         }
-        for (i = 0; i < state->num_windows; ++i) {
-            if (state->windows[i] == NULL)
-                continue;
-            Draw(&drawstates[i]);
-        }
-    }
-
+#endif
     /* Print out some timing information */
     now = SDL_GetTicks();
     if (now > then) {
--- a/test/testrendertarget.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testrendertarget.c	Sat Dec 13 01:17:10 2014 -0500
@@ -15,6 +15,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 
@@ -29,6 +33,10 @@
     int scale_direction;
 } DrawState;
 
+DrawState *drawstates;
+int done;
+SDL_bool test_composite = SDL_FALSE;
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -214,15 +222,33 @@
     return SDL_TRUE;
 }
 
+void
+loop()
+{
+    int i;
+    SDL_Event event;
+
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        if (state->windows[i] == NULL)
+            continue;
+        if (test_composite) {
+            if (!DrawComposite(&drawstates[i])) done = 1;
+        } else {
+            if (!Draw(&drawstates[i])) done = 1;
+        }
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    DrawState *drawstates;
-    int i, done;
-    SDL_Event event;
+    int i;
     int frames;
     Uint32 then, now;
-    SDL_bool test_composite = SDL_FALSE;
 
     /* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -278,22 +304,15 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
-        }
-        for (i = 0; i < state->num_windows; ++i) {
-            if (state->windows[i] == NULL)
-                continue;
-            if (test_composite) {
-                if (!DrawComposite(&drawstates[i])) done = 1;
-            } else {
-                if (!Draw(&drawstates[i])) done = 1;
-            }
-        }
+        loop();
     }
+#endif
 
     /* Print out some timing information */
     now = SDL_GetTicks();
--- a/test/testscale.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testscale.c	Sat Dec 13 01:17:10 2014 -0500
@@ -15,6 +15,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 #define WINDOW_WIDTH    640
@@ -31,6 +35,9 @@
     int scale_direction;
 } DrawState;
 
+DrawState *drawstates;
+int done;
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -120,12 +127,27 @@
     SDL_RenderPresent(s->renderer);
 }
 
+void
+loop()
+{
+    int i;
+    SDL_Event event;
+
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        if (state->windows[i] == NULL)
+            continue;
+        Draw(&drawstates[i]);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    DrawState *drawstates;
-    int i, done;
-    SDL_Event event;
+    int i;
     int frames;
     Uint32 then, now;
 
@@ -171,18 +193,15 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
-        }
-        for (i = 0; i < state->num_windows; ++i) {
-            if (state->windows[i] == NULL)
-                continue;
-            Draw(&drawstates[i]);
-        }
+        loop();
     }
+#endif
 
     /* Print out some timing information */
     now = SDL_GetTicks();
--- a/test/testsprite2.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testsprite2.c	Sat Dec 13 01:17:10 2014 -0500
@@ -15,6 +15,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test.h"
 #include "SDL_test_common.h"
 
@@ -38,6 +42,8 @@
 /* -1: infinite random moves (default); >=0: enables N deterministic moves */
 static int iterations = -1;
 
+int done;
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -230,11 +236,27 @@
     SDL_RenderPresent(renderer);
 }
 
+void
+loop()
+{
+    int i;
+    SDL_Event event;
+
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+    for (i = 0; i < state->num_windows; ++i) {
+        if (state->windows[i] == NULL)
+            continue;
+        MoveSprites(state->renderers[i], sprites[i]);
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    int i, done;
-    SDL_Event event;
+    int i;
     Uint32 then, now, frames;
 	Uint64 seed;
     const char *icon = "icon.bmp";
@@ -351,18 +373,15 @@
     frames = 0;
     then = SDL_GetTicks();
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
-        }
-        for (i = 0; i < state->num_windows; ++i) {
-            if (state->windows[i] == NULL)
-                continue;
-            MoveSprites(state->renderers[i], sprites[i]);
-        }
+        loop();
     }
+#endif
 
     /* Print out some timing information */
     now = SDL_GetTicks();
--- a/test/testspriteminimal.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testspriteminimal.c	Sat Dec 13 01:17:10 2014 -0500
@@ -15,6 +15,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL.h"
 
 #define WINDOW_WIDTH    640
@@ -27,6 +31,9 @@
 static SDL_Rect velocities[NUM_SPRITES];
 static int sprite_w, sprite_h;
 
+SDL_Renderer *renderer;
+int done;
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -118,13 +125,25 @@
     SDL_RenderPresent(renderer);
 }
 
+void loop()
+{
+    SDL_Event event;
+
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        if (event.type == SDL_QUIT || event.type == SDL_KEYDOWN) {
+            done = 1;
+        }
+    }
+    MoveSprites(renderer, sprite);
+}
+
 int
 main(int argc, char *argv[])
 {
     SDL_Window *window;
-    SDL_Renderer *renderer;
-    int i, done;
-    SDL_Event event;
+    int i;
+
 
 	/* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -154,16 +173,14 @@
 
     /* Main render loop */
     done = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
-        while (SDL_PollEvent(&event)) {
-            if (event.type == SDL_QUIT || event.type == SDL_KEYDOWN) {
-                done = 1;
-            }
+        loop();
         }
-        MoveSprites(renderer, sprite);
-    }
-
+#endif
     quit(0);
 
     return 0; /* to prevent compiler warning */
--- a/test/teststreaming.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/teststreaming.c	Sat Dec 13 01:17:10 2014 -0500
@@ -18,6 +18,10 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL.h"
 
 #define MOOSEPIC_W 64
@@ -52,6 +56,11 @@
 
 Uint8 MooseFrames[MOOSEFRAMES_COUNT][MOOSEFRAME_SIZE];
 
+SDL_Renderer *renderer;
+int frame;
+SDL_Texture *MooseTexture;
+SDL_bool done = SDL_FALSE;
+
 void quit(int rc)
 {
     SDL_Quit();
@@ -82,16 +91,37 @@
     SDL_UnlockTexture(texture);
 }
 
+void
+loop()
+{
+    SDL_Event event;
+
+    while (SDL_PollEvent(&event)) {
+        switch (event.type) {
+        case SDL_KEYDOWN:
+            if (event.key.keysym.sym == SDLK_ESCAPE) {
+                done = SDL_TRUE;
+            }
+            break;
+        case SDL_QUIT:
+            done = SDL_TRUE;
+            break;
+        }
+    }
+
+    frame = (frame + 1) % MOOSEFRAMES_COUNT;
+    UpdateTexture(MooseTexture, frame);
+
+    SDL_RenderClear(renderer);
+    SDL_RenderCopy(renderer, MooseTexture, NULL, NULL);
+    SDL_RenderPresent(renderer);
+}
+
 int
 main(int argc, char **argv)
 {
     SDL_Window *window;
-    SDL_Renderer *renderer;
     SDL_RWops *handle;
-    SDL_Texture *MooseTexture;
-    SDL_Event event;
-    SDL_bool done = SDL_FALSE;
-    int frame;
 
 	/* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -136,27 +166,15 @@
 
     /* Loop, waiting for QUIT or the escape key */
     frame = 0;
+
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        while (SDL_PollEvent(&event)) {
-            switch (event.type) {
-            case SDL_KEYDOWN:
-                if (event.key.keysym.sym == SDLK_ESCAPE) {
-                    done = SDL_TRUE;
-                }
-                break;
-            case SDL_QUIT:
-                done = SDL_TRUE;
-                break;
-            }
+        loop();
         }
-
-        frame = (frame + 1) % MOOSEFRAMES_COUNT;
-        UpdateTexture(MooseTexture, frame);
+#endif
 
-        SDL_RenderClear(renderer);
-        SDL_RenderCopy(renderer, MooseTexture, NULL, NULL);
-        SDL_RenderPresent(renderer);
-    }
     SDL_DestroyRenderer(renderer);
 
     quit(0);
--- a/test/testviewport.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testviewport.c	Sat Dec 13 01:17:10 2014 -0500
@@ -15,12 +15,23 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test.h"
 #include "SDL_test_common.h"
 
 
 static SDLTest_CommonState *state;
 
+SDL_Rect viewport;
+int done, j;
+SDL_bool use_target = SDL_FALSE;
+#ifdef __EMSCRIPTEN__
+Uint32 wait_start;
+#endif
+
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
 quit(int rc)
@@ -77,14 +88,54 @@
     SDL_RenderFillRect(renderer, &rect);
 }
 
+void
+loop()
+{
+#ifdef __EMSCRIPTEN__
+    /* Avoid using delays */
+    if(SDL_GetTicks() - wait_start < 1000)
+        return;
+    wait_start = SDL_GetTicks();
+#endif
+    SDL_Event event;
+    int i;
+    /* Check for events */
+    while (SDL_PollEvent(&event)) {
+        SDLTest_CommonEvent(state, &event, &done);
+    }
+
+    /* Move a viewport box in steps around the screen */
+    viewport.x = j * 100;
+    viewport.y = viewport.x;
+    viewport.w = 100 + j * 50;
+    viewport.h = 100 + j * 50;
+    j = (j + 1) % 4;
+    SDL_Log("Current Viewport x=%i y=%i w=%i h=%i", viewport.x, viewport.y, viewport.w, viewport.h);
+
+    for (i = 0; i < state->num_windows; ++i) {
+        if (state->windows[i] == NULL)
+            continue;
+
+        /* Draw using viewport */
+        DrawOnViewport(state->renderers[i], viewport);
+
+        /* Update the screen! */
+        if (use_target) {
+            SDL_SetRenderTarget(state->renderers[i], NULL);
+            SDL_RenderCopy(state->renderers[i], state->targets[i], NULL, NULL);
+            SDL_RenderPresent(state->renderers[i]);
+            SDL_SetRenderTarget(state->renderers[i], state->targets[i]);
+        } else {
+            SDL_RenderPresent(state->renderers[i]);
+        }
+    }
+}
+
 int
 main(int argc, char *argv[])
 {
-    int i, j, done;
-    SDL_Event event;
+    int i;
     Uint32 then, now, frames;
-    SDL_Rect viewport;
-    SDL_bool use_target = SDL_FALSE;
 
     /* Initialize test framework */
     state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
@@ -135,41 +186,17 @@
     then = SDL_GetTicks();
     done = 0;
     j = 0;
+
+#ifdef __EMSCRIPTEN__
+    wait_start = SDL_GetTicks();
+    emscripten_set_main_loop(loop, 0, 1);
+#else
     while (!done) {
-        /* Check for events */
         ++frames;
-        while (SDL_PollEvent(&event)) {
-            SDLTest_CommonEvent(state, &event, &done);
-        }
-        
-        /* Move a viewport box in steps around the screen */                
-        viewport.x = j * 100;
-        viewport.y = viewport.x;
-        viewport.w = 100 + j * 50;
-        viewport.h = 100 + j * 50;
-        j = (j + 1) % 4;            
-        SDL_Log("Current Viewport x=%i y=%i w=%i h=%i", viewport.x, viewport.y, viewport.w, viewport.h);
-        
-        for (i = 0; i < state->num_windows; ++i) {
-            if (state->windows[i] == NULL)
-                continue;
-                
-            /* Draw using viewport */        
-            DrawOnViewport(state->renderers[i], viewport);
-
-            /* Update the screen! */
-            if (use_target) {
-                SDL_SetRenderTarget(state->renderers[i], NULL);
-                SDL_RenderCopy(state->renderers[i], state->targets[i], NULL, NULL);
-                SDL_RenderPresent(state->renderers[i]);
-                SDL_SetRenderTarget(state->renderers[i], state->targets[i]);
-            } else {
-                SDL_RenderPresent(state->renderers[i]);
-            }
-        }
-        
+        loop();
         SDL_Delay(1000);
     }
+#endif
 
     /* Print out some timing information */
     now = SDL_GetTicks();
--- a/test/testwm2.c	Sat Dec 13 02:33:52 2014 -0500
+++ b/test/testwm2.c	Sat Dec 13 01:17:10 2014 -0500
@@ -13,22 +13,16 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
 #include "SDL_test_common.h"
 
 static SDLTest_CommonState *state;
+int done;
 
-/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
-static void
-quit(int rc)
-{
-    SDLTest_CommonQuit(state);
-    exit(rc);
-}
-
-int
-main(int argc, char *argv[])
-{
-    static const char *cursorNames[] = {
+static const char *cursorNames[] = {
         "arrow",
         "ibeam",
         "wait",
@@ -41,44 +35,22 @@
         "sizeALL",
         "NO",
         "hand",
-    };
-
-    int i, done;
-    SDL_Event event;
-    int system_cursor = -1;
-    SDL_Cursor *cursor = NULL;
-
-	/* Enable standard application logging */
-    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
-
-    SDL_assert(SDL_arraysize(cursorNames) == SDL_NUM_SYSTEM_CURSORS);
+};
+int system_cursor = -1;
+SDL_Cursor *cursor = NULL;
 
-    /* Initialize test framework */
-    state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
-    if (!state) {
-        return 1;
-    }
-    state->skip_renderer = SDL_TRUE;
-    for (i = 1; i < argc;) {
-        int consumed;
+/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
+static void
+quit(int rc)
+{
+    SDLTest_CommonQuit(state);
+    exit(rc);
+}
 
-        consumed = SDLTest_CommonArg(state, i);
-        if (consumed == 0) {
-            consumed = -1;
-        }
-        if (consumed < 0) {
-            SDL_Log("Usage: %s %s\n", argv[0], SDLTest_CommonUsage(state));
-            quit(1);
-        }
-        i += consumed;
-    }
-    if (!SDLTest_CommonInit(state)) {
-        quit(2);
-    }
-
-    /* Main render loop */
-    done = 0;
-    while (!done) {
+void
+loop()
+{
+    SDL_Event event;
         /* Check for events */
         while (SDL_PollEvent(&event)) {
             SDLTest_CommonEvent(state, &event, &done);
@@ -128,7 +100,50 @@
                 }
             }
         }
+}
+
+int
+main(int argc, char *argv[])
+{
+    int i;
+
+	/* Enable standard application logging */
+    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
+
+    SDL_assert(SDL_arraysize(cursorNames) == SDL_NUM_SYSTEM_CURSORS);
+
+    /* Initialize test framework */
+    state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
+    if (!state) {
+        return 1;
     }
+    state->skip_renderer = SDL_TRUE;
+    for (i = 1; i < argc;) {
+        int consumed;
+
+        consumed = SDLTest_CommonArg(state, i);
+        if (consumed == 0) {
+            consumed = -1;
+        }
+        if (consumed < 0) {
+            SDL_Log("Usage: %s %s\n", argv[0], SDLTest_CommonUsage(state));
+            quit(1);
+        }
+        i += consumed;
+    }
+    if (!SDLTest_CommonInit(state)) {
+        quit(2);
+    }
+
+    /* Main render loop */
+    done = 0;
+#ifdef __EMSCRIPTEN__
+    emscripten_set_main_loop(loop, 0, 1);
+#else
+    while (!done) {
+        loop();
+    }
+#endif
     SDL_FreeCursor(cursor);
 
     quit(0);