Browse Source

Merge commit '8f27ab75ada3af10c4f222b6cecebbd16ef466ab' into upstream.master

Yao Wei Tjong 姚伟忠 6 years ago
parent
commit
b67f85a59e
100 changed files with 10347 additions and 2627 deletions
  1. 153 45
      Source/ThirdParty/SDL/CMakeLists.txt
  2. 1 1
      Source/ThirdParty/SDL/COPYING.txt
  3. 117 0
      Source/ThirdParty/SDL/WhatsNew.txt
  4. 76 0
      Source/ThirdParty/SDL/android-project/app/src/main/AndroidManifest.xml
  5. 19 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java
  6. 649 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
  7. 668 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
  8. 315 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
  9. 84 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDL.java
  10. 2239 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
  11. 394 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
  12. 799 0
      Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
  13. 39 46
      Source/ThirdParty/SDL/cmake/sdlchecks.cmake
  14. 4 2
      Source/ThirdParty/SDL/include/SDL.h
  15. 1 1
      Source/ThirdParty/SDL/include/SDL_assert.h
  16. 23 2
      Source/ThirdParty/SDL/include/SDL_atomic.h
  17. 50 16
      Source/ThirdParty/SDL/include/SDL_audio.h
  18. 10 1
      Source/ThirdParty/SDL/include/SDL_bits.h
  19. 7 7
      Source/ThirdParty/SDL/include/SDL_blendmode.h
  20. 1 1
      Source/ThirdParty/SDL/include/SDL_clipboard.h
  21. 39 10
      Source/ThirdParty/SDL/include/SDL_config.h.cmake
  22. 0 157
      Source/ThirdParty/SDL/include/SDL_config_android.h
  23. 0 167
      Source/ThirdParty/SDL/include/SDL_config_iphoneos.h
  24. 0 199
      Source/ThirdParty/SDL/include/SDL_config_macosx.h
  25. 0 82
      Source/ThirdParty/SDL/include/SDL_config_minimal.h
  26. 0 129
      Source/ThirdParty/SDL/include/SDL_config_pandora.h
  27. 0 144
      Source/ThirdParty/SDL/include/SDL_config_psp.h
  28. 0 239
      Source/ThirdParty/SDL/include/SDL_config_windows.h
  29. 0 216
      Source/ThirdParty/SDL/include/SDL_config_winrt.h
  30. 0 122
      Source/ThirdParty/SDL/include/SDL_config_wiz.h
  31. 1 1
      Source/ThirdParty/SDL/include/SDL_copying.h
  32. 111 12
      Source/ThirdParty/SDL/include/SDL_cpuinfo.h
  33. 2 2
      Source/ThirdParty/SDL/include/SDL_egl.h
  34. 1 1
      Source/ThirdParty/SDL/include/SDL_endian.h
  35. 1 1
      Source/ThirdParty/SDL/include/SDL_error.h
  36. 38 1
      Source/ThirdParty/SDL/include/SDL_events.h
  37. 1 1
      Source/ThirdParty/SDL/include/SDL_filesystem.h
  38. 29 1
      Source/ThirdParty/SDL/include/SDL_gamecontroller.h
  39. 1 1
      Source/ThirdParty/SDL/include/SDL_gesture.h
  40. 15 4
      Source/ThirdParty/SDL/include/SDL_haptic.h
  41. 319 12
      Source/ThirdParty/SDL/include/SDL_hints.h
  42. 31 5
      Source/ThirdParty/SDL/include/SDL_joystick.h
  43. 1 1
      Source/ThirdParty/SDL/include/SDL_keyboard.h
  44. 1 1
      Source/ThirdParty/SDL/include/SDL_keycode.h
  45. 1 1
      Source/ThirdParty/SDL/include/SDL_loadso.h
  46. 1 1
      Source/ThirdParty/SDL/include/SDL_log.h
  47. 35 16
      Source/ThirdParty/SDL/include/SDL_main.h
  48. 1 1
      Source/ThirdParty/SDL/include/SDL_messagebox.h
  49. 1 1
      Source/ThirdParty/SDL/include/SDL_mouse.h
  50. 1 1
      Source/ThirdParty/SDL/include/SDL_mutex.h
  51. 1 1
      Source/ThirdParty/SDL/include/SDL_name.h
  52. 1 1
      Source/ThirdParty/SDL/include/SDL_opengl.h
  53. 1 1
      Source/ThirdParty/SDL/include/SDL_opengles.h
  54. 1 1
      Source/ThirdParty/SDL/include/SDL_opengles2.h
  55. 6 4
      Source/ThirdParty/SDL/include/SDL_pixels.h
  56. 7 2
      Source/ThirdParty/SDL/include/SDL_platform.h
  57. 1 1
      Source/ThirdParty/SDL/include/SDL_power.h
  58. 1 1
      Source/ThirdParty/SDL/include/SDL_quit.h
  59. 29 3
      Source/ThirdParty/SDL/include/SDL_rect.h
  60. 189 1
      Source/ThirdParty/SDL/include/SDL_render.h
  61. 50 13
      Source/ThirdParty/SDL/include/SDL_rwops.h
  62. 2 2
      Source/ThirdParty/SDL/include/SDL_scancode.h
  63. 251 0
      Source/ThirdParty/SDL/include/SDL_sensor.h
  64. 1 1
      Source/ThirdParty/SDL/include/SDL_shape.h
  65. 41 24
      Source/ThirdParty/SDL/include/SDL_stdinc.h
  66. 42 1
      Source/ThirdParty/SDL/include/SDL_surface.h
  67. 64 1
      Source/ThirdParty/SDL/include/SDL_system.h
  68. 17 14
      Source/ThirdParty/SDL/include/SDL_syswm.h
  69. 39 4
      Source/ThirdParty/SDL/include/SDL_thread.h
  70. 1 1
      Source/ThirdParty/SDL/include/SDL_timer.h
  71. 17 1
      Source/ThirdParty/SDL/include/SDL_touch.h
  72. 1 1
      Source/ThirdParty/SDL/include/SDL_types.h
  73. 2 2
      Source/ThirdParty/SDL/include/SDL_version.h
  74. 44 15
      Source/ThirdParty/SDL/include/SDL_video.h
  75. 41 20
      Source/ThirdParty/SDL/include/SDL_vulkan.h
  76. 4 1
      Source/ThirdParty/SDL/include/begin_code.h
  77. 4 1
      Source/ThirdParty/SDL/include/close_code.h
  78. 73 15
      Source/ThirdParty/SDL/src/SDL.c
  79. 23 6
      Source/ThirdParty/SDL/src/SDL_assert.c
  80. 6 1
      Source/ThirdParty/SDL/src/SDL_assert_c.h
  81. 1 1
      Source/ThirdParty/SDL/src/SDL_dataqueue.c
  82. 1 1
      Source/ThirdParty/SDL/src/SDL_dataqueue.h
  83. 27 1
      Source/ThirdParty/SDL/src/SDL_error.c
  84. 2 1
      Source/ThirdParty/SDL/src/SDL_error_c.h
  85. 1 1
      Source/ThirdParty/SDL/src/SDL_hints.c
  86. 6 2
      Source/ThirdParty/SDL/src/SDL_internal.h
  87. 21 8
      Source/ThirdParty/SDL/src/SDL_log.c
  88. 7 2
      Source/ThirdParty/SDL/src/atomic/SDL_atomic.c
  89. 27 3
      Source/ThirdParty/SDL/src/atomic/SDL_spinlock.c
  90. 115 45
      Source/ThirdParty/SDL/src/audio/SDL_audio.c
  91. 1 1
      Source/ThirdParty/SDL/src/audio/SDL_audio_c.h
  92. 18 8
      Source/ThirdParty/SDL/src/audio/SDL_audiocvt.c
  93. 1 1
      Source/ThirdParty/SDL/src/audio/SDL_audiodev.c
  94. 7 1
      Source/ThirdParty/SDL/src/audio/SDL_audiodev_c.h
  95. 671 60
      Source/ThirdParty/SDL/src/audio/SDL_audiotypecvt.c
  96. 1 1
      Source/ThirdParty/SDL/src/audio/SDL_mixer.c
  97. 6 4
      Source/ThirdParty/SDL/src/audio/SDL_sysaudio.h
  98. 2025 566
      Source/ThirdParty/SDL/src/audio/SDL_wave.c
  99. 104 32
      Source/ThirdParty/SDL/src/audio/SDL_wave.h
  100. 65 96
      Source/ThirdParty/SDL/src/audio/alsa/SDL_alsa_audio.c

+ 153 - 45
Source/ThirdParty/SDL/CMakeLists.txt

@@ -47,7 +47,7 @@
 
 # Urho3D - commented out setting SDL2 as project name as we want SDL sub-library to remain within Urho3D "umbrella" project
 #          instead, just enable the required language support and set the extra variables associated with project() command
-enable_language (C)
+enable_language (C CXX)
 set (SDL2_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 set (SDL2_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
 # Urho3D - commented out CheckTypeSize, FindPkgConfig, and GNUInstallDirs modules as the corresponding macros are not being used in our modified version
@@ -75,12 +75,14 @@ include(${SDL2_SOURCE_DIR}/cmake/sdlchecks.cmake)
 # set SDL_BINARY_AGE and SDL_INTERFACE_AGE to 0.
 set(SDL_MAJOR_VERSION 2)
 set(SDL_MINOR_VERSION 0)
-set(SDL_MICRO_VERSION 7)
+set(SDL_MICRO_VERSION 10)
 set(SDL_INTERFACE_AGE 0)
-set(SDL_BINARY_AGE 7)
+set(SDL_BINARY_AGE 10)
 set(SDL_VERSION "${SDL_MAJOR_VERSION}.${SDL_MINOR_VERSION}.${SDL_MICRO_VERSION}")
 
-# Urho3D - commented out CMAKE_DEBUG_POSTFIX configuration as our build system deos not not expose SDL as a library
+# Urho3D - commented out DYLIB's current_version and compatibility_version variables as we always build SDL as STATIC
+
+# Urho3D - commented out SDL_CMAKE_DEBUG_POSTFIX configuration as our build system does not not expose SDL as a library
 
 # Calculate a libtool-like version number
 math(EXPR LT_CURRENT "${SDL_MICRO_VERSION} - ${SDL_INTERFACE_AGE}")
@@ -90,7 +92,7 @@ set(LT_REVISION "${SDL_INTERFACE_AGE}")
 set(LT_RELEASE "${SDL_MAJOR_VERSION}.${SDL_MINOR_VERSION}")
 set(LT_VERSION "${LT_MAJOR}.${LT_AGE}.${LT_REVISION}")
 
-# Urho3D - commented out debug print
+#message(STATUS "${LT_VERSION} :: ${LT_AGE} :: ${LT_REVISION} :: ${LT_CURRENT} :: ${LT_RELEASE}")
 
 # General settings & flags
 # Urho3D - commented out LIBRARY_OUTPUT_DIRECTORY configuration so that it does not mess up with our global configuration setup
@@ -155,6 +157,20 @@ if(UNIX OR MINGW OR MSYS)
   set(OPT_DEF_LIBC ON)
 endif()
 
+# The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
+#  so we'll just use libusb when it's available. Except that libusb
+#  requires root permissions to open devices, so that's not generally
+#  useful, and we'll disable this by default on Unix. Windows and macOS
+#  can use it without root access, though, so enable by default there.
+if(WINDOWS OR APPLE OR ANDROID)
+  set(HIDAPI_SKIP_LIBUSB TRUE)
+else()
+  set(HIDAPI_SKIP_LIBUSB FALSE)
+endif()
+if (HIDAPI_SKIP_LIBUSB)
+  set(OPT_DEF_HIDAPI ON)
+endif()
+
 # Compiler info
 if(CMAKE_COMPILER_IS_GNUCC)
   set(USE_GCC TRUE)
@@ -221,13 +237,17 @@ if(EMSCRIPTEN)
   # loading architecture, low-level CPU inspection or multithreading.
   set(OPT_DEF_ASM FALSE)
   set(SDL_SHARED_ENABLED_BY_DEFAULT OFF)
-  # Urho3D - leave SDL_ATOMIC and SDL_LOADSO enable for Emscripten
+  # Urho3D - leave all the other subsystems enable by default
   set(SDL_THREADS_ENABLED_BY_DEFAULT OFF)
-  set(SDL_CPUINFO_ENABLED_BY_DEFAULT OFF)
 endif()
 
 # Urho3D - we always build internal sub-libraries as STATIC library type, so this build option is not applicable to us
+set (SDL_STATIC_ENABLED_BY_DEFAULT ON)
 set (SDL_SHARED_ENABLED_BY_DEFAULT OFF)
+# Urho3D - bug fix - add compiler defined expected by SDL.c when building STATIC librabry type
+if (WINDOWS)
+    add_definitions (-DSDL_STATIC_LIB)
+endif ()
 # Urho3D - we prefer to disable 2D render subsystem by default
 if (NOT DEFINED SDL_RENDER_ENABLED_BY_DEFAULT)
     set (SDL_RENDER_ENABLED_BY_DEFAULT OFF)
@@ -235,7 +255,7 @@ endif ()
 
 set(SDL_SUBSYSTEMS
     Atomic Audio Video Render Events Joystick Haptic Power Threads Timers
-    File Loadso CPUinfo Filesystem Dlopen)
+    File Loadso CPUinfo Filesystem Dlopen Sensor)
 foreach(_SUB ${SDL_SUBSYSTEMS})
   string(TOUPPER ${_SUB} _OPT)
   if (NOT DEFINED SDL_${_OPT}_ENABLED_BY_DEFAULT)
@@ -299,9 +319,6 @@ dep_option(VIDEO_WAYLAND       "Use Wayland video driver" ON UNIX_SYS OFF)
 dep_option(WAYLAND_SHARED      "Dynamically load Wayland support" ON VIDEO_WAYLAND OFF)
 # Urho3D - change the VIDEO_WAYLAND_QT_TOUCH default value to FALSE (this is only needed by Sailfish[?] but we don't know way to auto-detect that yet)
 dep_option(VIDEO_WAYLAND_QT_TOUCH  "QtWayland server support for Wayland video driver" OFF VIDEO_WAYLAND OFF)
-# Urho3D - change the VIDEO_MIR default value to FALSE (SDL requires the bleeding edge of MIR version which normal Linux distros do not provide)
-dep_option(VIDEO_MIR           "Use Mir video driver" OFF UNIX_SYS OFF)
-dep_option(MIR_SHARED          "Dynamically load Mir support" ON VIDEO_MIR OFF)
 # Urho3D - only make VIDEO_RPI option available on Raspberry-Pi platform
 dep_option(VIDEO_RPI           "Use Raspberry Pi video driver" ON RPI OFF)
 # Urho3D - bug fix - make VIDEO_X11 build option available for desktop UNIX (including OSX) only
@@ -320,6 +337,7 @@ if (WINDOWS)
 else ()
   set (DIRECTX OFF)  # Make it off explicitly when not targeting Windows platform, just in case user passes it in accidentally via CLI
 endif ()
+dep_option(WASAPI              "Use the Windows WASAPI audio driver" ON WINDOWS OFF)
 # Urho3D - commented out RENDER_D3D as an option to avoid potential conflict with our URHO3D_OPENGL and URHO3D_D3D11 build options on Windows platform
 #          Instead just initialize the variable according to our build options; Urho3D also by default disables the SDL renderer subsystem
 if (WINDOWS)
@@ -333,14 +351,17 @@ dep_option(VIDEO_VIVANTE       "Use Vivante EGL video driver" ON UNIX_SYS OFF)
 dep_option(VIDEO_VULKAN        "Enable Vulkan support" ON "ANDROID OR APPLE OR LINUX OR WINDOWS" OFF)
 dep_option(VIDEO_KMSDRM        "Use KMS DRM video driver" ON UNIX_SYS OFF)
 dep_option(KMSDRM_SHARED       "Dynamically load KMS DRM support" ON VIDEO_KMSDRM OFF)
+option_string(BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" OFF)
+option_string(FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF)
+set_option(HIDAPI              "Use HIDAPI for low level joystick drivers" ${OPT_DEF_HIDAPI})
 
-# 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 ${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")
+set(SDL_STATIC ${SDL_STATIC_ENABLED_BY_DEFAULT} CACHE BOOL "Build a static version of the library")
 
 dep_option(SDL_STATIC_PIC      "Static version of the library should be built with Position Independent Code" OFF "SDL_STATIC" OFF)
 
+# Urho3D - remove option to build the test directory
+
 # General source files
 set (SOURCE_FILE_PATTERNS
   ${SDL2_SOURCE_DIR}/src/*.c
@@ -351,7 +372,7 @@ set (SOURCE_FILE_PATTERNS
   ${SDL2_SOURCE_DIR}/src/thread/*.c
   ${SDL2_SOURCE_DIR}/src/timer/*.c)
 # Urho3D - exclude source files from disabled subsystems, except SDL_ATOMIC, SDL_THREADS and SDL_TIMERS as SDL always needs them even when they may be just generic or dummy implementations
-foreach (_SUB AUDIO CPUINFO EVENTS FILE HAPTIC JOYSTICK POWER RENDER VIDEO)
+foreach (_SUB AUDIO CPUINFO EVENTS FILE HAPTIC JOYSTICK POWER RENDER SENSOR VIDEO)
   string (TOLOWER ${_SUB} _DIR)
   if (${SDL_${_SUB}})
     list (APPEND SOURCE_FILE_PATTERNS ${SDL2_SOURCE_DIR}/src/${_DIR}/*.c)
@@ -360,7 +381,10 @@ endforeach ()
 if (SDL_RENDER)
   list (APPEND SOURCE_FILE_PATTERNS ${SDL2_SOURCE_DIR}/src/render/*/*.c)   # Recurse one more level
 endif ()
-file(GLOB SOURCE_FILES ${SOURCE_FILE_PATTERNS})
+if (SDL_VIDEO)
+  list (APPEND SOURCE_FILE_PATTERNS ${SDL2_SOURCE_DIR}/src/video/yuv2rgb/*.c)
+endif ()
+file (GLOB SOURCE_FILES ${SOURCE_FILE_PATTERNS})
 
 if(ASSERTIONS STREQUAL "auto")
   # Do nada - use optimization settings to determine the assertion level
@@ -377,6 +401,14 @@ else()
 endif()
 set(HAVE_ASSERTIONS ${ASSERTIONS})
 
+if(NOT BACKGROUNDING_SIGNAL STREQUAL "OFF")
+  add_definitions("-DSDL_BACKGROUNDING_SIGNAL=${BACKGROUNDING_SIGNAL}")
+endif()
+
+if(NOT FOREGROUNDING_SIGNAL STREQUAL "OFF")
+  add_definitions("-DSDL_FOREGROUNDING_SIGNAL=${FOREGROUNDING_SIGNAL}")
+endif()
+
 # Compiler option evaluation
 if(USE_GCC OR USE_CLANG)
   # Check for -Wall first, so later things can override pieces of it.
@@ -390,6 +422,11 @@ if(USE_GCC OR USE_CLANG)
     endif()
   endif()
 
+  check_c_compiler_flag(-fno-strict-aliasing HAVE_GCC_NO_STRICT_ALIASING)
+  if(HAVE_GCC_NO_STRICT_ALIASING)
+    list(APPEND EXTRA_CFLAGS "-fno-strict-aliasing")
+  endif()
+
   check_c_compiler_flag(-Wdeclaration-after-statement HAVE_GCC_WDECLARATION_AFTER_STATEMENT)
   if(HAVE_GCC_WDECLARATION_AFTER_STATEMENT)
     check_c_compiler_flag(-Werror=declaration-after-statement HAVE_GCC_WERROR_DECLARATION_AFTER_STATEMENT)
@@ -459,6 +496,8 @@ if(ASSEMBLY)
 
     # Urho3D - commented out the HAVE_SSEMATH variable as it is not being used anywhere currently; furthermore compiler already emits __SSE_MATH__ or __SSE2_MATH__ as necessary on x86_64 ABI only; so instead of using HAVE_SSEMATH variable, we should actually use the __SSE_MATH__ or __SSE2_MATH__ compiler define if that is the original intention of having this variable
 
+    check_include_file("immintrin.h" HAVE_IMMINTRIN_H)
+
     if(URHO3D_ALTIVEC)
       set(SDL_ALTIVEC_BLITTERS 1)
     endif()
@@ -500,8 +539,10 @@ if(LIBC)
             wcslen wcscmp
             strlen _strrev _strupr _strlwr strchr strrchr strstr itoa _ltoa
             _ultoa strtol strtoul strtoll strtod atoi atof strcmp strncmp
-            _stricmp _strnicmp sscanf atan atan2 acos asin ceil copysign cos
-            cosf fabs floor log pow scalbn sin sinf sqrt sqrtf tan tanf)
+            _stricmp _strnicmp sscanf
+            acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf
+            copysign copysignf cos cosf exp expf fabs fabsf floor floorf fmod fmodf
+            log logf log10 log10f pow powf scalbn scalbnf sin sinf sqrt sqrtf tan tanf)
       string(TOUPPER ${_FN} _UPPER)
       set(HAVE_${_UPPER} 1)
     endforeach()
@@ -529,7 +570,7 @@ if(LIBC)
     #          However, AppleClang when targeting iOS/tvOS platform can only find these headers correctly AFTER applying a CMake hack from our common module to workaround a CMake bug so that try_compile() command works well on iOS/tvOS platform
     foreach(_HEADER
             stdio.h stdlib.h stddef.h stdarg.h malloc.h memory.h string.h limits.h
-            strings.h wchar.h inttypes.h stdint.h ctype.h math.h iconv.h signal.h libunwind.h)
+            strings.h wchar.h inttypes.h stdint.h ctype.h math.h iconv.h signal.h)  # Urho3D - exclude libunwind.h that is only used for testing code which we don't include
       string(TOUPPER "HAVE_${_HEADER}" _UPPER)
       string(REPLACE "." "_" _HAVE_H ${_UPPER})
       check_include_file("${_HEADER}" ${_HAVE_H})
@@ -551,7 +592,7 @@ if(LIBC)
             _uitoa _ultoa strtol strtoul _i64toa _ui64toa strtoll strtoull
             atoi atof strcmp strncmp _stricmp strcasecmp _strnicmp strncasecmp
             vsscanf vsnprintf fopen64 fseeko fseeko64 sigaction setjmp
-            nanosleep sysconf sysctlbyname getauxval poll
+            nanosleep sysconf sysctlbyname getauxval poll _Exit
             )
       string(TOUPPER ${_FN} _UPPER)
       set(_HAVEVAR "HAVE_${_UPPER}")
@@ -603,6 +644,7 @@ foreach(_SUB ${SDL_SUBSYSTEMS})
     set(SDL_${_OPT}_DISABLED 1)
   endif()
 endforeach()
+# Urho3D - move the individual subsystem check to "General source files" block above
 
 # General SDL subsystem options, valid for all platforms
 if(SDL_AUDIO)
@@ -679,6 +721,11 @@ if(ANDROID)
     set(HAVE_SDL_HAPTIC TRUE)
   endif()
   if(SDL_JOYSTICK)
+    CheckHIDAPI()
+    # Urho3D - get the cue from how the hidapi is setup on other platforms
+    if (HAVE_HIDAPI)
+      list (APPEND SOURCE_FILES ${SDL2_SOURCE_DIR}/src/hidapi/android/hid.cpp)
+    endif ()
     set(SDL_JOYSTICK_ANDROID 1)
     file(GLOB ANDROID_JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/android/*.c ${SDL2_SOURCE_DIR}/src/joystick/steam/*.c)
     set(SOURCE_FILES ${SOURCE_FILES} ${ANDROID_JOYSTICK_SOURCES})
@@ -690,6 +737,12 @@ if(ANDROID)
     set(SOURCE_FILES ${SOURCE_FILES} ${ANDROID_POWER_SOURCES})
     set(HAVE_SDL_POWER TRUE)
   endif()
+  if(SDL_SENSOR)
+    set(SDL_SENSOR_ANDROID 1)
+    set(HAVE_SDL_SENSORS TRUE)
+    file(GLOB ANDROID_SENSOR_SOURCES ${SDL2_SOURCE_DIR}/src/sensor/android/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${ANDROID_SENSOR_SOURCES})
+  endif()
   if(SDL_VIDEO)
     set(SDL_VIDEO_DRIVER_ANDROID 1)
     file(GLOB ANDROID_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/android/*.c)
@@ -726,6 +779,13 @@ if(ANDROID)
       message(STATUS "Vulkan doesn't work on this configuration")
     endif()
   endif()
+
+  # Urho3D - import 'cpufeatures' from Android NDK
+  if (SDL_CPUINFO)
+    # This is a hack - intentionally do not depend on the 'cpufeatures' as separate built target with android_ndk_import_module_cpufeatures macro from NDK
+    list (APPEND INCLUDE_DIRS ${ANDROID_NDK}/sources/android/cpufeatures)
+    list (APPEND SOURCE_FILES ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
+  endif ()
 elseif(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.
@@ -798,6 +858,15 @@ elseif(APPLE)
   endif()
 
   if(SDL_JOYSTICK)
+    CheckHIDAPI()
+    # Urho3D - bug fix - make it work for iOS/tvOS platform
+    if(HAVE_HIDAPI)
+      if(IOS OR TVOS)
+        set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/hidapi/ios/hid.m)
+      else()
+        set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/hidapi/mac/hid.c)
+      endif()
+    endif()
     # Urho3D - bug fix - make it work for iOS/tvOS platform
     if (IOS OR TVOS)
       set (SDL_JOYSTICK_MFI 1)
@@ -809,7 +878,6 @@ elseif(APPLE)
       set(SDL_FRAMEWORK_FF 1)
     endif ()
     set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_SOURCES})
-    set_source_files_properties(${JOYSTICK_SOURCES} PROPERTIES LANGUAGE C)
     set(HAVE_SDL_JOYSTICK TRUE)
   endif()
 
@@ -884,6 +952,16 @@ elseif(APPLE)
     list(APPEND EXTRA_LIBS ${AUDIOTOOLBOX})
   endif()
 
+  # Urho3d - sync with SDL_config_iphoneos.h
+  if (SDL_SENSOR)
+    if (IOS)    # Intentionally do not let tvOS to go into the block
+      set(SDL_SENSOR_COREMOTION 1)
+      set(HAVE_SDL_SENSORS TRUE)
+      file(GLOB COREMOTION_SOURCES ${SDL2_SOURCE_DIR}/src/sensor/coremotion/*.m)
+      set(SOURCE_FILES ${SOURCE_FILES} ${COREMOTION_SOURCES})
+    endif ()
+  endif ()
+
   # Urho3d - bug fix - make it work for iOS/tvOS platform
   if(SDL_VIDEO)
     if (VIDEO_COCOA)
@@ -904,6 +982,22 @@ elseif(APPLE)
       set (SDL_VIDEO_RENDER_OGL_ES 1)
       set (SDL_VIDEO_RENDER_OGL_ES2 1)
       set (HAVE_VIDEO_OPENGLES TRUE)
+
+      # Urho3D - Metal supported on 64-bit devices running iOS 8.0 and tvOS 9.0 and newer
+      CHECK_C_SOURCE_COMPILES ("
+      #if (__IPHONE_OS_VERSION_MIN_REQUIRED < 80000) || (__TV_OS_VERSION_MIN_REQUIRED < 90000)
+      #error Metal doesn't work on this target platform
+      #endif
+      int main()
+      {
+          return 0;
+      }
+      " PASSED_METAL_CHECKS)
+      if (PASSED_METAL_CHECKS)
+        set (SDL_VIDEO_RENDER_METAL 1)
+      else ()
+        set (VIDEO_VULKAN OFF)
+      endif ()
     else ()
       CheckOpenGL ()
       CheckOpenGLES ()
@@ -980,7 +1074,6 @@ elseif(UNIX)    # Urho3D - at this point both UNIX and UNIX_SYS should be equiva
     # Need to check for Raspberry PI first and add platform specific compiler flags, otherwise the test for GLES fails!
     CheckRPI()
     CheckX11()
-    CheckMir()
     CheckDirectFB()
     # Urho3D - raname macros
     CheckOpenGL()
@@ -1069,6 +1162,7 @@ elseif(UNIX)    # Urho3D - at this point both UNIX and UNIX_SYS should be equiva
       set (SDL_USE_IME 1)
     endif ()
 
+    # Urho3D - comment out HAVE_LIBUNWIND_H check as it is always false in our case
     # Urho3D - bug fix - use find_path() to detect fcitx's header file so it works for both native and cross-compiling builds
     find_path (FCITX_H_INCLUDE_DIRS fcitx/frontend.h)
     if (FCITX_H_INCLUDE_DIRS)
@@ -1084,16 +1178,6 @@ elseif(UNIX)    # Urho3D - at this point both UNIX and UNIX_SYS should be equiva
       set(SOURCE_FILES ${SOURCE_FILES} ${POWER_SOURCES})
       set(HAVE_SDL_POWER TRUE)
     endif()
-    if(SDL_JOYSTICK)
-      set(SDL_JOYSTICK_LINUX 1)
-      file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/linux/*.c ${SDL2_SOURCE_DIR}/src/joystick/steam/*.c)
-      set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_SOURCES})
-      set(HAVE_SDL_JOYSTICK TRUE)
-    endif()
-  else ()
-    if(SDL_JOYSTICK)
-      CheckUSBHID()   # seems to be BSD specific - limit the test to BSD only?
-    endif()
   endif()
 
   if(INPUT_TSLIB)
@@ -1114,6 +1198,21 @@ elseif(UNIX)    # Urho3D - at this point both UNIX and UNIX_SYS should be equiva
     set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_TSLIB_SAVED})
   endif()
 
+  if(SDL_JOYSTICK)
+    CheckUSBHID()   # seems to be BSD specific - limit the test to BSD only?
+    CheckHIDAPI()
+    if(LINUX)
+      # Urho3D - get the cue from how the hidapi is setup on other platforms
+      if (HAVE_HIDAPI)
+        list (APPEND SOURCE_FILES ${SDL2_SOURCE_DIR}/src/hidapi/linux/hid.c ${SDL2_SOURCE_DIR}/src/hidapi/linux/hid.cpp ${SDL2_SOURCE_DIR}/src/hidapi/linux/hidraw.cpp)
+      endif ()    
+      set(SDL_JOYSTICK_LINUX 1)
+      file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/linux/*.c ${SDL2_SOURCE_DIR}/src/joystick/steam/*.c)
+      set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_SOURCES})
+      set(HAVE_SDL_JOYSTICK TRUE)
+    endif()
+  endif()
+
   # Urho3D - move CLOCK_GETTIME detection logic to compiler option evaluation section above
 
   # Urho3D - commented out setting of HAVE_LINUX_VERSION_H compiler define via cflags as that define is not being used at all, besides we prefer add_definitions()
@@ -1170,6 +1269,11 @@ elseif(WINDOWS)
     # Urho3D - TODO - move the find_package(DirectX) call from Urho3D common module to here later after we have refactored the library dependency handling
   endif()
 
+  # headers needed elsewhere ...
+  check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H)
+  check_include_file(audioclient.h HAVE_AUDIOCLIENT_H)
+  check_include_file(endpointvolume.h HAVE_ENDPOINTVOLUME_H)
+
   if(SDL_AUDIO)
     set(SDL_AUDIO_DRIVER_WINMM 1)
     file(GLOB WINMM_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/winmm/*.c)
@@ -1182,13 +1286,7 @@ elseif(WINDOWS)
       set(SOURCE_FILES ${SOURCE_FILES} ${DSOUND_AUDIO_SOURCES})
     endif()
 
-    if(HAVE_XAUDIO2_H)
-      set(SDL_AUDIO_DRIVER_XAUDIO2 1)
-      file(GLOB XAUDIO2_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/xaudio2/*.c)
-      set(SOURCE_FILES ${SOURCE_FILES} ${XAUDIO2_AUDIO_SOURCES})
-    endif()
-
-    if(HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H)
+    if(WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H)
       set(SDL_AUDIO_DRIVER_WASAPI 1)
       file(GLOB WASAPI_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/wasapi/*.c)
       set(SOURCE_FILES ${SOURCE_FILES} ${WASAPI_AUDIO_SOURCES})
@@ -1242,9 +1340,9 @@ elseif(WINDOWS)
   endif()
 
   # Libraries for Win32 native and MinGW
-  list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid)
+  list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32)
 
-  # TODO: in configure.in the check for timers is set on
+  # TODO: in configure.ac the check for timers is set on
   # cygwin | mingw32* - does this include mingw32CE?
   if(SDL_TIMERS)
     set(SDL_TIMER_WINDOWS 1)
@@ -1280,6 +1378,10 @@ elseif(WINDOWS)
   endif()
 
   if(SDL_JOYSTICK)
+    CheckHIDAPI()
+    if(HAVE_HIDAPI)
+      set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/hidapi/windows/hid.c)
+    endif()
     file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/windows/*.c)
     set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_SOURCES})
     if(HAVE_DINPUT_H)
@@ -1323,10 +1425,11 @@ endif()
 
 if(VIDEO_VULKAN)
   set(SDL_VIDEO_VULKAN 1)
+  set(HAVE_VIDEO_VULKAN TRUE)
 endif()
 
 # Dummies
-# configure.in does it differently:
+# configure.ac does it differently:
 # if not have X
 #   if enable_X {  SDL_X_DISABLED = 1 }
 #   [add dummy sources]
@@ -1334,17 +1437,22 @@ endif()
 # This leads to missing internal references on building, since the
 # src/X/*.c does not get included.
 if(NOT HAVE_SDL_JOYSTICK)
-  set(SDL_JOYSTICK_DISABLED 1)
+  set(SDL_JOYSTICK_DUMMY 1)
   if(SDL_JOYSTICK AND NOT APPLE) # results in unresolved symbols on OSX
     file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/dummy/*.c)
     set(SOURCE_FILES ${SOURCE_FILES} ${JOYSTICK_SOURCES})
   endif()
 endif()
 if(NOT HAVE_SDL_HAPTIC)
-  set(SDL_HAPTIC_DISABLED 1)
+  set(SDL_HAPTIC_DUMMY 1)
   file(GLOB HAPTIC_SOURCES ${SDL2_SOURCE_DIR}/src/haptic/dummy/*.c)
   set(SOURCE_FILES ${SOURCE_FILES} ${HAPTIC_SOURCES})
 endif()
+if(NOT HAVE_SDL_SENSORS)
+  set(SDL_SENSOR_DUMMY 1)
+  file(GLOB SENSORS_SOURCES ${SDL2_SOURCE_DIR}/src/sensor/dummy/*.c)
+  set(SOURCE_FILES ${SOURCE_FILES} ${SENSORS_SOURCES})
+endif()
 if(NOT HAVE_SDL_LOADSO)
   set(SDL_LOADSO_DISABLED 1)
   file(GLOB LOADSO_SOURCES ${SDL2_SOURCE_DIR}/src/loadso/dummy/*.c)
@@ -1377,7 +1485,7 @@ endif()
 #   endif()
 # endif()
 
-# Urho3D - use 'generated' path suffix for generated config header file (need the suffix as we support both out-of-source in-source build tree)
+# Urho3D - use 'generated' path suffix for generated config header file (need the suffix as we support both out-of-source and non out-of-source build tree)
 configure_file("${SDL2_SOURCE_DIR}/include/SDL_config.h.cmake"
   "${SDL2_BINARY_DIR}/include/generated/SDL_config.h")
 

+ 1 - 1
Source/ThirdParty/SDL/COPYING.txt

@@ -1,6 +1,6 @@
 
 Simple DirectMedia Layer
-Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
   
 This software is provided 'as-is', without any express or implied
 warranty.  In no event will the authors be held liable for any damages

+ 117 - 0
Source/ThirdParty/SDL/WhatsNew.txt

@@ -1,6 +1,123 @@
 
 This is a list of major changes in SDL's version history.
 
+---------------------------------------------------------------------------
+2.0.10:
+---------------------------------------------------------------------------
+
+General:
+* The SDL_RW* macros have been turned into functions that are available only in 2.0.10 and onward
+* Added SDL_SIMDGetAlignment(), SDL_SIMDAlloc(), and SDL_SIMDFree(), to allocate memory aligned for SIMD operations for the current CPU
+* Added SDL_RenderDrawPointF(), SDL_RenderDrawPointsF(), SDL_RenderDrawLineF(), SDL_RenderDrawLinesF(), SDL_RenderDrawRectF(), SDL_RenderDrawRectsF(), SDL_RenderFillRectF(), SDL_RenderFillRectsF(), SDL_RenderCopyF(), SDL_RenderCopyExF(), to allow floating point precision in the SDL rendering API.
+* Added SDL_GetTouchDeviceType() to get the type of a touch device, which can be a touch screen or a trackpad in relative or absolute coordinate mode.
+* The SDL rendering API now uses batched rendering by default, for improved performance
+* Added SDL_RenderFlush() to force batched render commands to execute, if you're going to mix SDL rendering with native rendering
+* Added the hint SDL_HINT_RENDER_BATCHING to control whether batching should be used for the rendering API. This defaults to "1" if you don't specify what rendering driver to use when creating the renderer.
+* Added the hint SDL_HINT_EVENT_LOGGING to enable logging of SDL events for debugging purposes
+* Added the hint SDL_HINT_GAMECONTROLLERCONFIG_FILE to specify a file that will be loaded at joystick initialization with game controller bindings
+* Added the hint SDL_HINT_MOUSE_TOUCH_EVENTS to control whether SDL will synthesize touch events from mouse events
+* Improved handling of malformed WAVE and BMP files, fixing potential security exploits
+
+Linux:
+* Removed the Mir video driver in favor of Wayland
+
+iOS / tvOS:
+* Added support for Xbox and PS4 wireless controllers in iOS 13 and tvOS 13
+* Added support for text input using Bluetooth keyboards
+
+Android:
+* Added low latency audio using OpenSL ES
+* Removed SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH (replaced by SDL_HINT_MOUSE_TOUCH_EVENTS and SDL_HINT_TOUCH_MOUSE_EVENTS)
+  SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH=1, should be replaced by setting both previous hints to 0.
+  SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH=0, should be replaced by setting both previous hints to 1.
+* Added the hint SDL_HINT_ANDROID_BLOCK_ON_PAUSE to set whether the event loop will block itself when the app is paused.
+
+
+---------------------------------------------------------------------------
+2.0.9:
+---------------------------------------------------------------------------
+
+General:
+* Added a new sensor API, initialized by passing SDL_INIT_SENSOR to SDL_Init(), and defined in SDL_sensor.h
+* Added an event SDL_SENSORUPDATE which is sent when a sensor is updated
+* Added SDL_GetDisplayOrientation() to return the current display orientation
+* Added an event SDL_DISPLAYEVENT which is sent when the display orientation changes
+* Added HIDAPI joystick drivers for more consistent support for Xbox, PS4 and Nintendo Switch Pro controller support across platforms. (Thanks to Valve for contributing the PS4 and Nintendo Switch Pro controller support)
+* Added support for many other popular game controllers
+* Added SDL_JoystickGetDevicePlayerIndex(), SDL_JoystickGetPlayerIndex(), and SDL_GameControllerGetPlayerIndex() to get the player index for a controller. For XInput controllers this returns the XInput index for the controller.
+* Added SDL_GameControllerRumble() and SDL_JoystickRumble() which allow simple rumble without using the haptics API
+* Added SDL_GameControllerMappingForDeviceIndex() to get the mapping for a controller before it's opened
+* Added the hint SDL_HINT_MOUSE_DOUBLE_CLICK_TIME to control the mouse double-click time
+* Added the hint SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS to control the mouse double-click radius, in pixels
+* Added SDL_HasColorKey() to return whether a surface has a colorkey active
+* Added SDL_HasAVX512F() to return whether the CPU has AVX-512F features
+* Added SDL_IsTablet() to return whether the application is running on a tablet
+* Added SDL_THREAD_PRIORITY_TIME_CRITICAL for threads that must run at the highest priority
+
+Mac OS X:
+* Fixed black screen at start on Mac OS X Mojave
+
+Linux:
+* Added SDL_LinuxSetThreadPriority() to allow adjusting the thread priority of native threads using RealtimeKit if available.
+
+iOS:
+* Fixed Asian IME input
+
+Android:
+* Updated required Android SDK to API 26, to match Google's new App Store requirements
+* Added support for wired USB Xbox, PS4, and Nintendo Switch Pro controllers
+* Added support for relative mouse mode on Android 7.0 and newer (except where it's broken, on Chromebooks and when in DeX mode with Samsung Experience 9.0)
+* Added support for custom mouse cursors on Android 7.0 and newer
+* Added the hint SDL_HINT_ANDROID_TRAP_BACK_BUTTON to control whether the back button will back out of the app (the default) or be passed to the application as SDL_SCANCODE_AC_BACK
+* Added SDL_AndroidBackButton() to trigger the Android system back button behavior when handling the back button in the application
+* Added SDL_IsChromebook() to return whether the app is running in the Chromebook Android runtime
+* Added SDL_IsDeXMode() to return whether the app is running while docked in the Samsung DeX
+
+
+---------------------------------------------------------------------------
+2.0.8:
+---------------------------------------------------------------------------
+
+General:
+* Added SDL_fmod() and SDL_log10()
+* Each of the SDL math functions now has the corresponding float version
+* Added SDL_SetYUVConversionMode() and SDL_GetYUVConversionMode() to control the formula used when converting to and from YUV colorspace. The options are JPEG, BT.601, and BT.709
+
+Windows:
+* Implemented WASAPI support on Windows UWP and removed the deprecated XAudio2 implementation
+* Added resampling support on WASAPI on Windows 7 and above
+
+Windows UWP:
+* Added SDL_WinRTGetDeviceFamily() to find out what type of device your application is running on
+
+Mac OS X:
+* Added support for the Vulkan SDK for Mac:
+  https://www.lunarg.com/lunarg-releases-vulkan-sdk-1-0-69-0-for-mac/
+* Added support for OpenGL ES using ANGLE when it's available
+
+Mac OS X / iOS / tvOS:
+* Added a Metal 2D render implementation
+* Added SDL_RenderGetMetalLayer() and SDL_RenderGetMetalCommandEncoder() to insert your own drawing into SDL rendering when using the Metal implementation
+
+iOS:
+* Added the hint SDL_HINT_IOS_HIDE_HOME_INDICATOR to control whether the home indicator bar on iPhone X should be hidden. This defaults to dimming the indicator for fullscreen applications and showing the indicator for windowed applications.
+
+iOS / Android:
+* Added the hint SDL_HINT_RETURN_KEY_HIDES_IME to control whether the return key on the software keyboard should hide the keyboard or send a key event (the default)
+
+Android:
+* SDL now supports building with Android Studio and Gradle by default, and the old Ant project is available in android-project-ant
+* SDL now requires the API 19 SDK to build, but can still target devices down to API 14 (Android 4.0.1)
+* Added SDL_IsAndroidTV() to tell whether the application is running on Android TV
+
+Android / tvOS:
+* Added the hint SDL_HINT_TV_REMOTE_AS_JOYSTICK to control whether TV remotes should be listed as joystick devices (the default) or send keyboard events.
+
+Linux:
+* Added the hint SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR to control whether the X server should skip the compositor for the SDL application. This defaults to "1"
+* Added the hint SDL_HINT_VIDEO_DOUBLE_BUFFER to control whether the Raspberry Pi and KMSDRM video drivers should use double or triple buffering (the default)
+
+
 ---------------------------------------------------------------------------
 2.0.7:
 ---------------------------------------------------------------------------

+ 76 - 0
Source/ThirdParty/SDL/android-project/app/src/main/AndroidManifest.xml

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Replace com.test.game with the identifier of your game below, e.g.
+     com.gamemaker.game
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.libsdl.app"
+    android:versionCode="1"
+    android:versionName="1.0"
+    android:installLocation="auto">
+
+    <!-- OpenGL ES 2.0 -->
+    <uses-feature android:glEsVersion="0x00020000" />
+
+    <!-- Touchscreen support -->
+    <uses-feature
+        android:name="android.hardware.touchscreen"
+        android:required="false" />
+
+    <!-- Game controller support -->
+    <uses-feature
+        android:name="android.hardware.gamepad"
+        android:required="false" />
+
+    <!-- External mouse input events -->
+    <uses-feature
+        android:name="android.hardware.type.pc"
+        android:required="false" />
+
+    <!-- Allow writing to external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <!-- Allow access to the vibrator -->
+    <uses-permission android:name="android.permission.VIBRATE" />
+
+    <!-- if you want to capture audio, uncomment this. -->
+    <!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
+
+    <!-- Create a Java class extending SDLActivity and place it in a
+         directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
+ 
+         then replace "SDLActivity" with the name of your class (e.g. "MyGame")
+         in the XML below.
+
+         An example Java class can be found in README-android.md
+    -->
+    <application android:label="@string/app_name"
+        android:icon="@mipmap/ic_launcher"
+        android:allowBackup="true"
+        android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+        android:hardwareAccelerated="true" >
+
+        <!-- Example of setting SDL hints from AndroidManifest.xml:
+        <meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
+         -->
+     
+        <activity android:name="SDLActivity"
+            android:label="@string/app_name"
+            android:alwaysRetainTaskState="true"
+            android:launchMode="singleInstance"
+            android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
+            >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <!-- Drop file event -->
+            <!--
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="*/*" />
+            </intent-filter>
+            -->
+        </activity>
+    </application>
+
+</manifest>

+ 19 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java

@@ -0,0 +1,19 @@
+package org.libsdl.app;
+
+interface HIDDevice
+{
+    public int getId();
+    public int getVendorId();
+    public int getProductId();
+    public String getSerialNumber();
+    public int getVersion();
+    public String getManufacturerName();
+    public String getProductName();
+    public boolean open();
+    public int sendFeatureReport(byte[] report);
+    public int sendOutputReport(byte[] report);
+    public boolean getFeatureReport(byte[] report);
+    public void setFrozen(boolean frozen);
+    public void close();
+    public void shutdown();
+}

+ 649 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java

@@ -0,0 +1,649 @@
+// Modified by Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothGattService;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.os.*;
+
+//import com.android.internal.util.HexDump;
+
+import java.lang.Runnable;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.UUID;
+
+// Urho3D - suppress lint error
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
+
+    private static final String TAG = "hidapi";
+    private HIDDeviceManager mManager;
+    private BluetoothDevice mDevice;
+    private int mDeviceId;
+    private BluetoothGatt mGatt;
+    private boolean mIsRegistered = false;
+    private boolean mIsConnected = false;
+    private boolean mIsChromebook = false;
+    private boolean mIsReconnecting = false;
+    private boolean mFrozen = false;
+    private LinkedList<GattOperation> mOperations;
+    GattOperation mCurrentOperation = null;
+    private Handler mHandler;
+
+    private static final int TRANSPORT_AUTO = 0;
+    private static final int TRANSPORT_BREDR = 1;
+    private static final int TRANSPORT_LE = 2;
+
+    private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
+
+    static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
+    static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
+    static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
+    static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
+
+    static class GattOperation {
+        private enum Operation {
+            CHR_READ,
+            CHR_WRITE,
+            ENABLE_NOTIFICATION
+        }
+
+        Operation mOp;
+        UUID mUuid;
+        byte[] mValue;
+        BluetoothGatt mGatt;
+        boolean mResult = true;
+
+        private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
+            mGatt = gatt;
+            mOp = operation;
+            mUuid = uuid;
+        }
+
+        private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
+            mGatt = gatt;
+            mOp = operation;
+            mUuid = uuid;
+            mValue = value;
+        }
+
+        public void run() {
+            // This is executed in main thread
+            BluetoothGattCharacteristic chr;
+
+            switch (mOp) {
+                case CHR_READ:
+                    chr = getCharacteristic(mUuid);
+                    //Log.v(TAG, "Reading characteristic " + chr.getUuid());
+                    if (!mGatt.readCharacteristic(chr)) {
+                        Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
+                        mResult = false;
+                        break;
+                    }
+                    mResult = true;
+                    break;
+                case CHR_WRITE:
+                    chr = getCharacteristic(mUuid);
+                    //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
+                    chr.setValue(mValue);
+                    if (!mGatt.writeCharacteristic(chr)) {
+                        Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
+                        mResult = false;
+                        break;
+                    }
+                    mResult = true;
+                    break;
+                case ENABLE_NOTIFICATION:
+                    chr = getCharacteristic(mUuid);
+                    //Log.v(TAG, "Writing descriptor of " + chr.getUuid());
+                    if (chr != null) {
+                        BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
+                        if (cccd != null) {
+                            int properties = chr.getProperties();
+                            byte[] value;
+                            if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
+                                value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
+                            } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
+                                value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
+                            } else {
+                                Log.e(TAG, "Unable to start notifications on input characteristic");
+                                mResult = false;
+                                return;
+                            }
+
+                            mGatt.setCharacteristicNotification(chr, true);
+                            cccd.setValue(value);
+                            if (!mGatt.writeDescriptor(cccd)) {
+                                Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
+                                mResult = false;
+                                return;
+                            }
+                            mResult = true;
+                        }
+                    }
+            }
+        }
+
+        public boolean finish() {
+            return mResult;
+        }
+
+        private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+            BluetoothGattService valveService = mGatt.getService(steamControllerService);
+            if (valveService == null)
+                return null;
+            return valveService.getCharacteristic(uuid);
+        }
+
+        static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
+            return new GattOperation(gatt, Operation.CHR_READ, uuid);
+        }
+
+        static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
+            return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
+        }
+
+        static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
+            return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
+        }
+    }
+
+    public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
+        mManager = manager;
+        mDevice = device;
+        mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
+        mIsRegistered = false;
+        mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+        mOperations = new LinkedList<GattOperation>();
+        mHandler = new Handler(Looper.getMainLooper());
+
+        mGatt = connectGatt();
+        final HIDDeviceBLESteamController finalThis = this;
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                finalThis.checkConnectionForChromebookIssue();
+            }
+        }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
+    }
+
+    public String getIdentifier() {
+        return String.format("SteamController.%s", mDevice.getAddress());
+    }
+
+    public BluetoothGatt getGatt() {
+        return mGatt;
+    }
+
+    // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
+    // of TRANSPORT_LE.  Let's force ourselves to connect low energy.
+    private BluetoothGatt connectGatt(boolean managed) {
+        if (Build.VERSION.SDK_INT >= 23) {
+            try {
+                return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
+            } catch (Exception e) {
+                return mDevice.connectGatt(mManager.getContext(), managed, this);
+            }
+        } else {
+            return mDevice.connectGatt(mManager.getContext(), managed, this);
+        }
+    }
+
+    private BluetoothGatt connectGatt() {
+        return connectGatt(false);
+    }
+
+    protected int getConnectionState() {
+
+        Context context = mManager.getContext();
+        if (context == null) {
+            // We are lacking any context to get our Bluetooth information.  We'll just assume disconnected.
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
+        if (btManager == null) {
+            // This device doesn't support Bluetooth.  We should never be here, because how did
+            // we instantiate a device to start with?
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
+    }
+
+    public void reconnect() {
+
+        if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+            mGatt.disconnect();
+            mGatt = connectGatt();
+        }
+
+    }
+
+    protected void checkConnectionForChromebookIssue() {
+        if (!mIsChromebook) {
+            // We only do this on Chromebooks, because otherwise it's really annoying to just attempt
+            // over and over.
+            return;
+        }
+
+        int connectionState = getConnectionState();
+
+        switch (connectionState) {
+            case BluetoothProfile.STATE_CONNECTED:
+                if (!mIsConnected) {
+                    // We are in the Bad Chromebook Place.  We can force a disconnect
+                    // to try to recover.
+                    Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback.  Forcing a reconnect.");
+                    mIsReconnecting = true;
+                    mGatt.disconnect();
+                    mGatt = connectGatt(false);
+                    break;
+                }
+                else if (!isRegistered()) {
+                    if (mGatt.getServices().size() > 0) {
+                        Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration.  Trying to recover.");
+                        probeService(this);
+                    }
+                    else {
+                        Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services.  Trying to recover.");
+                        mIsReconnecting = true;
+                        mGatt.disconnect();
+                        mGatt = connectGatt(false);
+                        break;
+                    }
+                }
+                else {
+                    Log.v(TAG, "Chromebook: We are connected, and registered.  Everything's good!");
+                    return;
+                }
+                break;
+
+            case BluetoothProfile.STATE_DISCONNECTED:
+                Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us.  Attempting a disconnect/reconnect, but we may not be able to recover.");
+
+                mIsReconnecting = true;
+                mGatt.disconnect();
+                mGatt = connectGatt(false);
+                break;
+
+            case BluetoothProfile.STATE_CONNECTING:
+                Log.v(TAG, "Chromebook: We're still trying to connect.  Waiting a bit longer.");
+                break;
+        }
+
+        final HIDDeviceBLESteamController finalThis = this;
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                finalThis.checkConnectionForChromebookIssue();
+            }
+        }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
+    }
+
+    private boolean isRegistered() {
+        return mIsRegistered;
+    }
+
+    private void setRegistered() {
+        mIsRegistered = true;
+    }
+
+    private boolean probeService(HIDDeviceBLESteamController controller) {
+
+        if (isRegistered()) {
+            return true;
+        }
+
+        if (!mIsConnected) {
+            return false;
+        }
+
+        Log.v(TAG, "probeService controller=" + controller);
+
+        for (BluetoothGattService service : mGatt.getServices()) {
+            if (service.getUuid().equals(steamControllerService)) {
+                Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
+
+                for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
+                    if (chr.getUuid().equals(inputCharacteristic)) {
+                        Log.v(TAG, "Found input characteristic");
+                        // Start notifications
+                        BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
+                        if (cccd != null) {
+                            enableNotification(chr.getUuid());
+                        }
+                    }
+                }
+                return true;
+            }
+        }
+
+        if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
+            Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
+            mIsConnected = false;
+            mIsReconnecting = true;
+            mGatt.disconnect();
+            mGatt = connectGatt(false);
+        }
+
+        return false;
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void finishCurrentGattOperation() {
+        GattOperation op = null;
+        synchronized (mOperations) {
+            if (mCurrentOperation != null) {
+                op = mCurrentOperation;
+                mCurrentOperation = null;
+            }
+        }
+        if (op != null) {
+            boolean result = op.finish(); // TODO: Maybe in main thread as well?
+
+            // Our operation failed, let's add it back to the beginning of our queue.
+            if (!result) {
+                mOperations.addFirst(op);
+            }
+        }
+        executeNextGattOperation();
+    }
+
+    private void executeNextGattOperation() {
+        synchronized (mOperations) {
+            if (mCurrentOperation != null)
+                return;
+
+            if (mOperations.isEmpty())
+                return;
+
+            mCurrentOperation = mOperations.removeFirst();
+        }
+
+        // Run in main thread
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mOperations) {
+                    if (mCurrentOperation == null) {
+                        Log.e(TAG, "Current operation null in executor?");
+                        return;
+                    }
+
+                    mCurrentOperation.run();
+                    // now wait for the GATT callback and when it comes, finish this operation
+                }
+            }
+        });
+    }
+
+    private void queueGattOperation(GattOperation op) {
+        synchronized (mOperations) {
+            mOperations.add(op);
+        }
+        executeNextGattOperation();
+    }
+
+    private void enableNotification(UUID chrUuid) {
+        GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
+        queueGattOperation(op);
+    }
+
+    public void writeCharacteristic(UUID uuid, byte[] value) {
+        GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
+        queueGattOperation(op);
+    }
+
+    public void readCharacteristic(UUID uuid) {
+        GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
+        queueGattOperation(op);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////  BluetoothGattCallback overridden methods
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
+        //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
+        mIsReconnecting = false;
+        if (newState == 2) {
+            mIsConnected = true;
+            // Run directly, without GattOperation
+            if (!isRegistered()) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mGatt.discoverServices();
+                    }
+                });
+            }
+        } 
+        else if (newState == 0) {
+            mIsConnected = false;
+        }
+
+        // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
+    }
+
+    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+        //Log.v(TAG, "onServicesDiscovered status=" + status);
+        if (status == 0) {
+            if (gatt.getServices().size() == 0) {
+                Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
+                mIsReconnecting = true;
+                mIsConnected = false;
+                gatt.disconnect();
+                mGatt = connectGatt(false);
+            }
+            else {
+                probeService(this);
+            }
+        }
+    }
+
+    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+        //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
+
+        if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
+            mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
+        }
+
+        finishCurrentGattOperation();
+    }
+
+    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+        //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
+
+        if (characteristic.getUuid().equals(reportCharacteristic)) {
+            // Only register controller with the native side once it has been fully configured
+            if (!isRegistered()) {
+                Log.v(TAG, "Registering Steam Controller with ID: " + getId());
+                mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0);
+                setRegistered();
+            }
+        }
+
+        finishCurrentGattOperation();
+    }
+
+    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+    // Enable this for verbose logging of controller input reports
+        //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
+
+        if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
+            mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
+        }
+    }
+
+    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+        //Log.v(TAG, "onDescriptorRead status=" + status);
+    }
+
+    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+        BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
+        //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
+
+        if (chr.getUuid().equals(inputCharacteristic)) {
+            boolean hasWrittenInputDescriptor = true;
+            BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
+            if (reportChr != null) {
+                Log.v(TAG, "Writing report characteristic to enter valve mode");
+                reportChr.setValue(enterValveMode);
+                gatt.writeCharacteristic(reportChr);
+            }
+        }
+
+        finishCurrentGattOperation();
+    }
+
+    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+        //Log.v(TAG, "onReliableWriteCompleted status=" + status);
+    }
+
+    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+        //Log.v(TAG, "onReadRemoteRssi status=" + status);
+    }
+
+    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+        //Log.v(TAG, "onMtuChanged status=" + status);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////// Public API
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public int getId() {
+        return mDeviceId;
+    }
+
+    @Override
+    public int getVendorId() {
+        // Valve Corporation
+        final int VALVE_USB_VID = 0x28DE;
+        return VALVE_USB_VID;
+    }
+
+    @Override
+    public int getProductId() {
+        // We don't have an easy way to query from the Bluetooth device, but we know what it is
+        final int D0G_BLE2_PID = 0x1106;
+        return D0G_BLE2_PID;
+    }
+
+    @Override
+    public String getSerialNumber() {
+        // This will be read later via feature report by Steam
+        return "12345";
+    }
+
+    @Override
+    public int getVersion() {
+        return 0;
+    }
+
+    @Override
+    public String getManufacturerName() {
+        return "Valve Corporation";
+    }
+
+    @Override
+    public String getProductName() {
+        return "Steam Controller";
+    }
+
+    @Override
+    public boolean open() {
+        return true;
+    }
+
+    @Override
+    public int sendFeatureReport(byte[] report) {
+        if (!isRegistered()) {
+            Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
+            if (mIsConnected) {
+                probeService(this);
+            }
+            return -1;
+        }
+
+        // We need to skip the first byte, as that doesn't go over the air
+        byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
+        //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
+        writeCharacteristic(reportCharacteristic, actual_report);
+        return report.length;
+    }
+
+    @Override
+    public int sendOutputReport(byte[] report) {
+        if (!isRegistered()) {
+            Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
+            if (mIsConnected) {
+                probeService(this);
+            }
+            return -1;
+        }
+
+        //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
+        writeCharacteristic(reportCharacteristic, report);
+        return report.length;
+    }
+
+    @Override
+    public boolean getFeatureReport(byte[] report) {
+        if (!isRegistered()) {
+            Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
+            if (mIsConnected) {
+                probeService(this);
+            }
+            return false;
+        }
+
+        //Log.v(TAG, "getFeatureReport");
+        readCharacteristic(reportCharacteristic);
+        return true;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public void setFrozen(boolean frozen) {
+        mFrozen = frozen;
+    }
+
+    @Override
+    public void shutdown() {
+        close();
+
+        BluetoothGatt g = mGatt;
+        if (g != null) {
+            g.disconnect();
+            g.close();
+            mGatt = null;
+        }
+        mManager = null;
+        mIsRegistered = false;
+        mIsConnected = false;
+        mOperations.clear();
+    }
+
+}
+

+ 668 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java

@@ -0,0 +1,668 @@
+// Modified by Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.os.Build;
+import android.util.Log;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.hardware.usb.*;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HIDDeviceManager {
+    private static final String TAG = "hidapi";
+    private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
+
+    private static HIDDeviceManager sManager;
+    private static int sManagerRefCount = 0;
+
+    public static HIDDeviceManager acquire(Context context) {
+        if (sManagerRefCount == 0) {
+            sManager = new HIDDeviceManager(context);
+        }
+        ++sManagerRefCount;
+        return sManager;
+    }
+
+    public static void release(HIDDeviceManager manager) {
+        if (manager == sManager) {
+            --sManagerRefCount;
+            if (sManagerRefCount == 0) {
+                sManager.close();
+                sManager = null;
+            }
+        }
+    }
+
+    private Context mContext;
+    private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
+    private HashMap<UsbDevice, HIDDeviceUSB> mUSBDevices = new HashMap<UsbDevice, HIDDeviceUSB>();
+    private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
+    private int mNextDeviceId = 0;
+    private SharedPreferences mSharedPreferences = null;
+    private boolean mIsChromebook = false;
+    private UsbManager mUsbManager;
+    private Handler mHandler;
+    private BluetoothManager mBluetoothManager;
+    private List<BluetoothDevice> mLastBluetoothDevices;
+
+    private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceAttached(usbDevice);
+            } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
+                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceDetached(usbDevice);
+            } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
+                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
+            }
+        }
+    };
+
+    private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            // Bluetooth device was connected. If it was a Steam Controller, handle it
+            if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                Log.d(TAG, "Bluetooth device connected: " + device);
+
+                if (isSteamController(device)) {
+                    connectBluetoothDevice(device);
+                }
+            }
+
+            // Bluetooth device was disconnected, remove from controller manager (if any)
+            if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                Log.d(TAG, "Bluetooth device disconnected: " + device);
+
+                disconnectBluetoothDevice(device);
+            }
+        }
+    };
+
+    private HIDDeviceManager(final Context context) {
+        mContext = context;
+
+        // Urho3D - HIDAPI is built-in inside our custom SDL library, so no need to load libhidapi.so
+
+        HIDDeviceRegisterCallback();
+
+        mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
+        mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+
+//        if (shouldClear) {
+//            SharedPreferences.Editor spedit = mSharedPreferences.edit();
+//            spedit.clear();
+//            spedit.commit();
+//        }
+//        else
+        {
+            mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
+        }
+
+        initializeUSB();
+
+        // Urho3D - check first if Bluetooth is available
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            initializeBluetooth();
+        }
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public int getDeviceIDForIdentifier(String identifier) {
+        SharedPreferences.Editor spedit = mSharedPreferences.edit();
+
+        int result = mSharedPreferences.getInt(identifier, 0);
+        if (result == 0) {
+            result = mNextDeviceId++;
+            spedit.putInt("next_device_id", mNextDeviceId);
+        }
+
+        spedit.putInt(identifier, result);
+        spedit.commit();
+        return result;
+    }
+
+    private void initializeUSB() {
+        mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
+
+        /*
+        // Logging
+        for (UsbDevice device : mUsbManager.getDeviceList().values()) {
+            Log.i(TAG,"Path: " + device.getDeviceName());
+            Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
+            Log.i(TAG,"Product: " + device.getProductName());
+            Log.i(TAG,"ID: " + device.getDeviceId());
+            Log.i(TAG,"Class: " + device.getDeviceClass());
+            Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
+            Log.i(TAG,"Vendor ID " + device.getVendorId());
+            Log.i(TAG,"Product ID: " + device.getProductId());
+            Log.i(TAG,"Interface count: " + device.getInterfaceCount());
+            Log.i(TAG,"---------------------------------------");
+
+            // Get interface details
+            for (int index = 0; index < device.getInterfaceCount(); index++) {
+                UsbInterface mUsbInterface = device.getInterface(index);
+                Log.i(TAG,"  *****     *****");
+                Log.i(TAG,"  Interface index: " + index);
+                Log.i(TAG,"  Interface ID: " + mUsbInterface.getId());
+                Log.i(TAG,"  Interface class: " + mUsbInterface.getInterfaceClass());
+                Log.i(TAG,"  Interface subclass: " + mUsbInterface.getInterfaceSubclass());
+                Log.i(TAG,"  Interface protocol: " + mUsbInterface.getInterfaceProtocol());
+                Log.i(TAG,"  Endpoint count: " + mUsbInterface.getEndpointCount());
+
+                // Get endpoint details 
+                for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
+                {
+                    UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
+                    Log.i(TAG,"    ++++   ++++   ++++");
+                    Log.i(TAG,"    Endpoint index: " + epi);
+                    Log.i(TAG,"    Attributes: " + mEndpoint.getAttributes());
+                    Log.i(TAG,"    Direction: " + mEndpoint.getDirection());
+                    Log.i(TAG,"    Number: " + mEndpoint.getEndpointNumber());
+                    Log.i(TAG,"    Interval: " + mEndpoint.getInterval());
+                    Log.i(TAG,"    Packet size: " + mEndpoint.getMaxPacketSize());
+                    Log.i(TAG,"    Type: " + mEndpoint.getType());
+                }
+            }
+        }
+        Log.i(TAG," No more devices connected.");
+        */
+
+        // Register for USB broadcasts and permission completions
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
+        mContext.registerReceiver(mUsbBroadcast, filter);
+
+        for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
+            handleUsbDeviceAttached(usbDevice);
+        }
+    }
+
+    UsbManager getUSBManager() {
+        return mUsbManager;
+    }
+
+    private void shutdownUSB() {
+        try {
+            mContext.unregisterReceiver(mUsbBroadcast);
+        } catch (Exception e) {
+            // We may not have registered, that's okay
+        }
+    }
+
+    private boolean isHIDDeviceUSB(UsbDevice usbDevice) {
+        for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) {
+            if (isHIDDeviceInterface(usbDevice, interface_number)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) {
+        UsbInterface usbInterface = usbDevice.getInterface(interface_number);
+        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
+            return true;
+        }
+        if (interface_number == 0) {
+            if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
+        final int XB360_IFACE_SUBCLASS = 93;
+        final int XB360_IFACE_PROTOCOL = 1; // Wired only
+        final int[] SUPPORTED_VENDORS = {
+            0x0079, // GPD Win 2
+            0x044f, // Thrustmaster
+            0x045e, // Microsoft
+            0x046d, // Logitech
+            0x056e, // Elecom
+            0x06a3, // Saitek
+            0x0738, // Mad Catz
+            0x07ff, // Mad Catz
+            0x0e6f, // Unknown
+            0x0f0d, // Hori
+            0x11c9, // Nacon
+            0x12ab, // Unknown
+            0x1430, // RedOctane
+            0x146b, // BigBen
+            0x1532, // Razer Sabertooth
+            0x15e4, // Numark
+            0x162e, // Joytech
+            0x1689, // Razer Onza
+            0x1bad, // Harmonix
+            0x24c6, // PowerA
+        };
+
+        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
+            usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
+            usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
+            int vendor_id = usbDevice.getVendorId();
+            for (int supportedVid : SUPPORTED_VENDORS) {
+                if (vendor_id == supportedVid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
+        final int XB1_IFACE_SUBCLASS = 71;
+        final int XB1_IFACE_PROTOCOL = 208;
+        final int[] SUPPORTED_VENDORS = {
+            0x045e, // Microsoft
+            0x0738, // Mad Catz
+            0x0e6f, // Unknown
+            0x0f0d, // Hori
+            0x1532, // Razer Wildcat
+            0x24c6, // PowerA
+        };
+
+        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
+            usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
+            usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
+            int vendor_id = usbDevice.getVendorId();
+            for (int supportedVid : SUPPORTED_VENDORS) {
+                if (vendor_id == supportedVid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void handleUsbDeviceAttached(UsbDevice usbDevice) {
+        if (isHIDDeviceUSB(usbDevice)) {
+            connectHIDDeviceUSB(usbDevice);
+        }
+    }
+
+    private void handleUsbDeviceDetached(UsbDevice usbDevice) {
+        HIDDeviceUSB device = mUSBDevices.get(usbDevice);
+        if (device == null)
+            return;
+
+        int id = device.getId();
+        mUSBDevices.remove(usbDevice);
+        mDevicesById.remove(id);
+        device.shutdown();
+        HIDDeviceDisconnected(id);
+    }
+
+    private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
+        HIDDeviceUSB device = mUSBDevices.get(usbDevice);
+        if (device == null)
+            return;
+
+        boolean opened = false;
+        if (permission_granted) {
+            opened = device.open();
+        }
+        HIDDeviceOpenResult(device.getId(), opened);
+    }
+
+    private void connectHIDDeviceUSB(UsbDevice usbDevice) {
+        synchronized (this) {
+            for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) {
+                if (isHIDDeviceInterface(usbDevice, interface_number)) {
+                    HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_number);
+                    int id = device.getId();
+                    mUSBDevices.put(usbDevice, device);
+                    mDevicesById.put(id, device);
+                    HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number);
+                    break;
+                }
+            }
+        }
+    }
+
+    // Urho3D - suppress lint error
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+    private void initializeBluetooth() {
+        Log.d(TAG, "Initializing Bluetooth");
+
+        if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+            Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
+            return;
+        }
+
+        // Find bonded bluetooth controllers and create SteamControllers for them
+        mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+        if (mBluetoothManager == null) {
+            // This device doesn't support Bluetooth.
+            return;
+        }
+
+        BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
+        if (btAdapter == null) {
+            // This device has Bluetooth support in the codebase, but has no available adapters.
+            return;
+        }
+
+        // Get our bonded devices.
+        for (BluetoothDevice device : btAdapter.getBondedDevices()) {
+
+            Log.d(TAG, "Bluetooth device available: " + device);
+            if (isSteamController(device)) {
+                connectBluetoothDevice(device);
+            }
+
+        }
+
+        // NOTE: These don't work on Chromebooks, to my undying dismay.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mContext.registerReceiver(mBluetoothBroadcast, filter);
+
+        if (mIsChromebook) {
+            mHandler = new Handler(Looper.getMainLooper());
+            mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
+
+            // final HIDDeviceManager finalThis = this;
+            // mHandler.postDelayed(new Runnable() {
+            //     @Override
+            //     public void run() {
+            //         finalThis.chromebookConnectionHandler();
+            //     }
+            // }, 5000);
+        }
+    }
+
+    private void shutdownBluetooth() {
+        try {
+            mContext.unregisterReceiver(mBluetoothBroadcast);
+        } catch (Exception e) {
+            // We may not have registered, that's okay
+        }
+    }
+
+    // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
+    // This function provides a sort of dummy version of that, watching for changes in the
+    // connected devices and attempting to add controllers as things change.
+    // Urho3D - suppress lint error
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public void chromebookConnectionHandler() {
+        if (!mIsChromebook) {
+            return;
+        }
+
+        ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
+        ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
+
+        List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
+
+        for (BluetoothDevice bluetoothDevice : currentConnected) {
+            if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
+                connected.add(bluetoothDevice);
+            }
+        }
+        for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
+            if (!currentConnected.contains(bluetoothDevice)) {
+                disconnected.add(bluetoothDevice);
+            }
+        }
+
+        mLastBluetoothDevices = currentConnected;
+
+        for (BluetoothDevice bluetoothDevice : disconnected) {
+            disconnectBluetoothDevice(bluetoothDevice);
+        }
+        for (BluetoothDevice bluetoothDevice : connected) {
+            connectBluetoothDevice(bluetoothDevice);
+        }
+
+        final HIDDeviceManager finalThis = this;
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                finalThis.chromebookConnectionHandler();
+            }
+        }, 10000);
+    }
+
+    public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
+        synchronized (this) {
+            if (mBluetoothDevices.containsKey(bluetoothDevice)) {
+                Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
+
+                HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
+                device.reconnect();
+
+                return false;
+            }
+            HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
+            int id = device.getId();
+            mBluetoothDevices.put(bluetoothDevice, device);
+            mDevicesById.put(id, device);
+
+            // The Steam Controller will mark itself connected once initialization is complete
+        }
+        return true;
+    }
+
+    public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        synchronized (this) {
+            HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
+            if (device == null)
+                return;
+
+            int id = device.getId();
+            mBluetoothDevices.remove(bluetoothDevice);
+            mDevicesById.remove(id);
+            device.shutdown();
+            HIDDeviceDisconnected(id);
+        }
+    }
+
+    // Urho3D - suppress lint error
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public boolean isSteamController(BluetoothDevice bluetoothDevice) {
+        // Sanity check.  If you pass in a null device, by definition it is never a Steam Controller.
+        if (bluetoothDevice == null) {
+            return false;
+        }
+
+        // If the device has no local name, we really don't want to try an equality check against it.
+        if (bluetoothDevice.getName() == null) {
+            return false;
+        }
+
+        return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
+    }
+
+    private void close() {
+        shutdownUSB();
+        shutdownBluetooth();
+        synchronized (this) {
+            for (HIDDevice device : mDevicesById.values()) {
+                device.shutdown();
+            }
+            mDevicesById.clear();
+            mBluetoothDevices.clear();
+            HIDDeviceReleaseCallback();
+        }
+    }
+
+    public void setFrozen(boolean frozen) {
+        synchronized (this) {
+            for (HIDDevice device : mDevicesById.values()) {
+                device.setFrozen(frozen);
+            }
+        }        
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private HIDDevice getDevice(int id) {
+        synchronized (this) {
+            HIDDevice result = mDevicesById.get(id);
+            if (result == null) {
+                Log.v(TAG, "No device for id: " + id);
+                Log.v(TAG, "Available devices: " + mDevicesById.keySet());
+            }
+            return result;
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    ////////// JNI interface functions
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public boolean openDevice(int deviceID) {
+        // Look to see if this is a USB device and we have permission to access it
+        for (HIDDeviceUSB device : mUSBDevices.values()) {
+            if (deviceID == device.getId()) {
+                UsbDevice usbDevice = device.getDevice();
+                if (!mUsbManager.hasPermission(usbDevice)) {
+                    HIDDeviceOpenPending(deviceID);
+                    try {
+                        mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0));
+                    } catch (Exception e) {
+                        Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
+                        HIDDeviceOpenResult(deviceID, false);
+                    }
+                    return false;
+                }
+                break;
+            }
+        }
+
+        try {
+            Log.v(TAG, "openDevice deviceID=" + deviceID);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return false;
+            }
+
+            return device.open();
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return false;
+    }
+
+    public int sendOutputReport(int deviceID, byte[] report) {
+        try {
+            Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return -1;
+            }
+
+            return device.sendOutputReport(report);
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return -1;
+    }
+
+    public int sendFeatureReport(int deviceID, byte[] report) {
+        try {
+            Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return -1;
+            }
+
+            return device.sendFeatureReport(report);
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return -1;
+    }
+
+    public boolean getFeatureReport(int deviceID, byte[] report) {
+        try {
+            Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return false;
+            }
+
+            return device.getFeatureReport(report);
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return false;
+    }
+
+    public void closeDevice(int deviceID) {
+        try {
+            Log.v(TAG, "closeDevice deviceID=" + deviceID);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return;
+            }
+
+            device.close();
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    /////////////// Native methods
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private native void HIDDeviceRegisterCallback();
+    private native void HIDDeviceReleaseCallback();
+
+    native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number);
+    native void HIDDeviceOpenPending(int deviceID);
+    native void HIDDeviceOpenResult(int deviceID, boolean opened);
+    native void HIDDeviceDisconnected(int deviceID);
+
+    native void HIDDeviceInputReport(int deviceID, byte[] report);
+    native void HIDDeviceFeatureReport(int deviceID, byte[] report);
+}

+ 315 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java

@@ -0,0 +1,315 @@
+// Modified by Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import android.hardware.usb.*;
+import android.os.Build;
+import android.util.Log;
+import java.util.Arrays;
+
+class HIDDeviceUSB implements HIDDevice {
+
+    private static final String TAG = "hidapi";
+
+    protected HIDDeviceManager mManager;
+    protected UsbDevice mDevice;
+    protected int mInterface;
+    protected int mDeviceId;
+    protected UsbDeviceConnection mConnection;
+    protected UsbEndpoint mInputEndpoint;
+    protected UsbEndpoint mOutputEndpoint;
+    protected InputThread mInputThread;
+    protected boolean mRunning;
+    protected boolean mFrozen;
+
+    public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_number) {
+        mManager = manager;
+        mDevice = usbDevice;
+        mInterface = interface_number;
+        mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
+        mRunning = false;
+    }
+
+    public String getIdentifier() {
+        return String.format("%s/%x/%x", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId());
+    }
+
+    @Override
+    public int getId() {
+        return mDeviceId;
+    }
+
+    @Override
+    public int getVendorId() {
+        return mDevice.getVendorId();
+    }
+
+    @Override
+    public int getProductId() {
+        return mDevice.getProductId();
+    }
+
+    @Override
+    public String getSerialNumber() {
+        String result = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            result = mDevice.getSerialNumber();
+        }
+        if (result == null) {
+            result = "";
+        }
+        return result;
+    }
+
+    @Override
+    public int getVersion() {
+        return 0;
+    }
+
+    @Override
+    public String getManufacturerName() {
+        String result = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            result = mDevice.getManufacturerName();
+        }
+        if (result == null) {
+            result = String.format("%x", getVendorId());
+        }
+        return result;
+    }
+
+    @Override
+    public String getProductName() {
+        String result = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            result = mDevice.getProductName();
+        }
+        if (result == null) {
+            result = String.format("%x", getProductId());
+        }
+        return result;
+    }
+
+    public UsbDevice getDevice() {
+        return mDevice;
+    }
+
+    public String getDeviceName() {
+        return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
+    }
+
+    @Override
+    public boolean open() {
+        mConnection = mManager.getUSBManager().openDevice(mDevice);
+        if (mConnection == null) {
+            Log.w(TAG, "Unable to open USB device " + getDeviceName());
+            return false;
+        }
+
+        // Force claim all interfaces
+        for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+            UsbInterface iface = mDevice.getInterface(i);
+
+            if (!mConnection.claimInterface(iface, true)) {
+                Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
+                close();
+                return false;
+            }
+        }
+
+        // Find the endpoints
+        UsbInterface iface = mDevice.getInterface(mInterface);
+        for (int j = 0; j < iface.getEndpointCount(); j++) {
+            UsbEndpoint endpt = iface.getEndpoint(j);
+            switch (endpt.getDirection()) {
+            case UsbConstants.USB_DIR_IN:
+                if (mInputEndpoint == null) {
+                    mInputEndpoint = endpt;
+                }
+                break;
+            case UsbConstants.USB_DIR_OUT:
+                if (mOutputEndpoint == null) {
+                    mOutputEndpoint = endpt;
+                }
+                break;
+            }
+        }
+
+        // Make sure the required endpoints were present
+        if (mInputEndpoint == null || mOutputEndpoint == null) {
+            Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
+            close();
+            return false;
+        }
+
+        // Start listening for input
+        mRunning = true;
+        mInputThread = new InputThread();
+        mInputThread.start();
+
+        return true;
+    }
+
+    @Override
+    public int sendFeatureReport(byte[] report) {
+        int res = -1;
+        int offset = 0;
+        int length = report.length;
+        boolean skipped_report_id = false;
+        byte report_number = report[0];
+
+        if (report_number == 0x0) {
+            ++offset;
+            --length;
+            skipped_report_id = true;
+        }
+
+      // Urho3D - check first if the new API is available
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+        res = mConnection.controlTransfer(
+            UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
+            0x09/*HID set_report*/,
+            (3/*HID feature*/ << 8) | report_number,
+            0,
+            report, offset, length,
+            1000/*timeout millis*/);
+      }
+
+        if (res < 0) {
+            Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
+            return -1;
+        }
+
+        if (skipped_report_id) {
+            ++length;
+        }
+        return length;
+    }
+
+    @Override
+    public int sendOutputReport(byte[] report) {
+        int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
+        if (r != report.length) {
+            Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
+        }
+        return r;
+    }
+
+    @Override
+    public boolean getFeatureReport(byte[] report) {
+        int res = -1;
+        int offset = 0;
+        int length = report.length;
+        boolean skipped_report_id = false;
+        byte report_number = report[0];
+
+        if (report_number == 0x0) {
+            /* Offset the return buffer by 1, so that the report ID
+               will remain in byte 0. */
+            ++offset;
+            --length;
+            skipped_report_id = true;
+        }
+
+      // Urho3D - check first if the new API is available
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+        res = mConnection.controlTransfer(
+            UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
+            0x01/*HID get_report*/,
+            (3/*HID feature*/ << 8) | report_number,
+            0,
+            report, offset, length,
+            1000/*timeout millis*/);
+      }
+
+        if (res < 0) {
+            Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
+            return false;
+        }
+
+        if (skipped_report_id) {
+            ++res;
+            ++length;
+        }
+
+        byte[] data;
+        if (res == length) {
+            data = report;
+        } else {
+            data = Arrays.copyOfRange(report, 0, res);
+        }
+        mManager.HIDDeviceFeatureReport(mDeviceId, data);
+
+        return true;
+    }
+
+    @Override
+    public void close() {
+        mRunning = false;
+        if (mInputThread != null) {
+            while (mInputThread.isAlive()) {
+                mInputThread.interrupt();
+                try {
+                    mInputThread.join();
+                } catch (InterruptedException e) {
+                    // Keep trying until we're done
+                }
+            }
+            mInputThread = null;
+        }
+        if (mConnection != null) {
+            for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+                UsbInterface iface = mDevice.getInterface(i);
+                mConnection.releaseInterface(iface);
+            }
+            mConnection.close();
+            mConnection = null;
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        close();
+        mManager = null;
+    }
+
+    @Override
+    public void setFrozen(boolean frozen) {
+        mFrozen = frozen;
+    }
+
+    protected class InputThread extends Thread {
+        @Override
+        public void run() {
+            int packetSize = mInputEndpoint.getMaxPacketSize();
+            byte[] packet = new byte[packetSize];
+            while (mRunning) {
+                int r;
+                try
+                {
+                    r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
+                }
+                catch (Exception e)
+                {
+                    Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
+                    break;
+                }
+                if (r < 0) {
+                    // Could be a timeout or an I/O error
+                }
+                if (r > 0) {
+                    byte[] data;
+                    if (r == packetSize) {
+                        data = packet;
+                    } else {
+                        data = Arrays.copyOfRange(packet, 0, r);
+                    }
+
+                    if (!mFrozen) {
+                        mManager.HIDDeviceInputReport(mDeviceId, data);
+                    }
+                }
+            }
+        }
+    }
+}

+ 84 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDL.java

@@ -0,0 +1,84 @@
+package org.libsdl.app;
+
+import android.content.Context;
+
+import java.lang.reflect.*;
+
+/**
+    SDL library initialization
+*/
+public class SDL {
+
+    // This function should be called first and sets up the native code
+    // so it can call into the Java classes
+    public static void setupJNI() {
+        SDLActivity.nativeSetupJNI();
+        SDLAudioManager.nativeSetupJNI();
+        SDLControllerManager.nativeSetupJNI();
+    }
+
+    // This function should be called each time the activity is started
+    public static void initialize() {
+        setContext(null);
+
+        SDLActivity.initialize();
+        SDLAudioManager.initialize();
+        SDLControllerManager.initialize();
+    }
+
+    // This function stores the current activity (SDL or not)
+    public static void setContext(Context context) {
+        mContext = context;
+    }
+
+    public static Context getContext() {
+        return mContext;
+    }
+
+    public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
+
+        if (libraryName == null) {
+            throw new NullPointerException("No library name provided.");
+        }
+
+        try {
+            // Let's see if we have ReLinker available in the project.  This is necessary for 
+            // some projects that have huge numbers of local libraries bundled, and thus may 
+            // trip a bug in Android's native library loader which ReLinker works around.  (If
+            // loadLibrary works properly, ReLinker will simply use the normal Android method
+            // internally.)
+            //
+            // To use ReLinker, just add it as a dependency.  For more information, see 
+            // https://github.com/KeepSafe/ReLinker for ReLinker's repository.
+            //
+            Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
+            Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
+            Class contextClass = mContext.getClassLoader().loadClass("android.content.Context");
+            Class stringClass = mContext.getClassLoader().loadClass("java.lang.String");
+
+            // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if 
+            // they've changed during updates.
+            Method forceMethod = relinkClass.getDeclaredMethod("force");
+            Object relinkInstance = forceMethod.invoke(null);
+            Class relinkInstanceClass = relinkInstance.getClass();
+
+            // Actually load the library!
+            Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
+            loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
+        }
+        catch (final Throwable e) {
+            // Fall back
+            try {
+                System.loadLibrary(libraryName);
+            }
+            catch (final UnsatisfiedLinkError ule) {
+                throw ule;
+            }
+            catch (final SecurityException se) {
+                throw se;
+            }
+        }        
+    }
+
+    protected static Context mContext;
+}

+ 2239 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java

@@ -0,0 +1,2239 @@
+// Modified by Lasse Oorni and Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+import java.lang.reflect.Method;
+import java.lang.Math;
+import java.util.List;
+
+import android.app.*;
+import android.content.*;
+import android.content.res.Configuration;
+import android.text.InputType;
+import android.view.*;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.RelativeLayout;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.os.*;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
+import android.graphics.*;
+import android.graphics.drawable.Drawable;
+import android.hardware.*;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ApplicationInfo;
+
+import com.github.urho3d.UrhoActivity;
+
+/**
+    SDL Activity
+*/
+public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
+    private static final String TAG = "SDL";
+
+    public static boolean mIsResumedCalled, mHasFocus;
+    public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24);
+
+    // Cursor types
+    private static final int SDL_SYSTEM_CURSOR_NONE = -1;
+    private static final int SDL_SYSTEM_CURSOR_ARROW = 0;
+    private static final int SDL_SYSTEM_CURSOR_IBEAM = 1;
+    private static final int SDL_SYSTEM_CURSOR_WAIT = 2;
+    private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3;
+    private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4;
+    private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5;
+    private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6;
+    private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7;
+    private static final int SDL_SYSTEM_CURSOR_SIZENS = 8;
+    private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9;
+    private static final int SDL_SYSTEM_CURSOR_NO = 10;
+    private static final int SDL_SYSTEM_CURSOR_HAND = 11;
+
+    protected static final int SDL_ORIENTATION_UNKNOWN = 0;
+    protected static final int SDL_ORIENTATION_LANDSCAPE = 1;
+    protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2;
+    protected static final int SDL_ORIENTATION_PORTRAIT = 3;
+    protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4;
+
+    protected static int mCurrentOrientation;
+
+    // Handle the state of the native layer
+    public enum NativeState {
+           INIT, RESUMED, PAUSED
+    }
+
+    public static NativeState mNextNativeState;
+    public static NativeState mCurrentNativeState;
+
+    /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
+    public static boolean mBrokenLibraries;
+
+    // Main components
+    protected static SDLActivity mSingleton;
+    protected static SDLSurface mSurface;
+    protected static View mTextEdit;
+    protected static boolean mScreenKeyboardShown;
+    protected static ViewGroup mLayout;
+    protected static SDLClipboardHandler mClipboardHandler;
+    protected static Hashtable<Integer, PointerIcon> mCursors;
+    protected static int mLastCursorID;
+    protected static SDLGenericMotionListener_API12 mMotionListener;
+    protected static HIDDeviceManager mHIDDeviceManager;
+
+    // This is what SDL runs in. It invokes SDL_main(), eventually
+    protected static Thread mSDLThread;
+
+    protected static SDLGenericMotionListener_API12 getMotionListener() {
+        if (mMotionListener == null) {
+            if (Build.VERSION.SDK_INT >= 26) {
+                mMotionListener = new SDLGenericMotionListener_API26();
+            } else
+            if (Build.VERSION.SDK_INT >= 24) {
+                mMotionListener = new SDLGenericMotionListener_API24();
+            } else {
+                mMotionListener = new SDLGenericMotionListener_API12();
+            }
+        }
+
+        return mMotionListener;
+    }
+
+    // Urho3D - default implementation returns the last shared lib being loaded
+    private static String mMainSharedLib;
+
+    /**
+     * This method returns the name of the shared object with the application entry point
+     * It can be overridden by derived classes.
+     */
+    protected String getMainSharedObject() {
+        // Urho3D - should not be called before the library is loaded.
+        return mMainSharedLib;
+    }
+
+    /**
+     * This method returns the name of the application entry point
+     * It can be overridden by derived classes.
+     */
+    protected String getMainFunction() {
+        return "SDL_main";
+    }
+
+    // Urho3D - avoid hardcoding of the library list
+    protected void onLoadLibrary(List<String> libraryNames) {
+        for (final String name : libraryNames) {
+            SDL.loadLibrary(name);
+        }
+        mMainSharedLib = "lib" + libraryNames.get(libraryNames.size() - 1) + ".so";
+    }
+
+    /**
+     * This method is called by SDL before starting the native application thread.
+     * It can be overridden to provide the arguments after the application name.
+     * The default implementation returns an empty array. It never returns null.
+     * @return arguments for the native application.
+     */
+    protected String[] getArguments() {
+        // Urho3D - the default implementation returns the "app_process" as the first argument instead of empty array
+        return new String[]{"app_process"};
+    }
+
+    public static void initialize() {
+        // The static nature of the singleton and Android quirkyness force us to initialize everything here
+        // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
+        mSingleton = null;
+        mSurface = null;
+        mTextEdit = null;
+        mLayout = null;
+        mClipboardHandler = null;
+        mCursors = new Hashtable<Integer, PointerIcon>();
+        mLastCursorID = 0;
+        mSDLThread = null;
+        mBrokenLibraries = false;
+        mIsResumedCalled = false;
+        mHasFocus = true;
+        mNextNativeState = NativeState.INIT;
+        mCurrentNativeState = NativeState.INIT;
+    }
+
+    // Setup
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.v(TAG, "Device: " + Build.DEVICE);
+        Log.v(TAG, "Model: " + Build.MODEL);
+        Log.v(TAG, "onCreate()");
+        super.onCreate(savedInstanceState);
+
+        try {
+            Thread.currentThread().setName("SDLActivity");
+        } catch (Exception e) {
+            Log.v(TAG, "modify thread properties failed " + e.toString());
+        }
+
+        // Urho3D - auto load all the shared libraries available in the library path
+        String errorMsgBrokenLib = "";
+        try {
+            onLoadLibrary(UrhoActivity.getLibraryNames(this));
+        } catch(Exception e) {
+            mBrokenLibraries = true;
+            errorMsgBrokenLib = e.getMessage();
+        }
+
+        if (mBrokenLibraries)
+        {
+            mSingleton = this;
+            AlertDialog.Builder dlgAlert  = new AlertDialog.Builder(this);
+            dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
+                  + System.getProperty("line.separator")
+                  + System.getProperty("line.separator")
+                  + "Error: " + errorMsgBrokenLib);
+            dlgAlert.setTitle("SDL Error");
+            dlgAlert.setPositiveButton("Exit",
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog,int id) {
+                        // if this button is clicked, close current activity
+                        SDLActivity.mSingleton.finish();
+                    }
+                });
+           dlgAlert.setCancelable(false);
+           dlgAlert.create().show();
+
+           return;
+        }
+
+        // Set up JNI
+        SDL.setupJNI();
+
+        // Initialize state
+        SDL.initialize();
+
+        // So we can call stuff from static callbacks
+        mSingleton = this;
+        SDL.setContext(this);
+
+        mClipboardHandler = new SDLClipboardHandler_API11();
+
+        mHIDDeviceManager = HIDDeviceManager.acquire(this);
+
+        // Set up the surface
+        mSurface = new SDLSurface(getApplication());
+
+        mLayout = new RelativeLayout(this);
+        mLayout.addView(mSurface);
+
+        // Get our current screen orientation and pass it down.
+        mCurrentOrientation = SDLActivity.getCurrentOrientation();
+        // Only record current orientation
+        SDLActivity.onNativeOrientationChanged(mCurrentOrientation);
+
+        setContentView(mLayout);
+
+        setWindowStyle(false);
+
+        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
+
+        // Get filename from "Open with" of another application
+        Intent intent = getIntent();
+        if (intent != null && intent.getData() != null) {
+            String filename = intent.getData().getPath();
+            if (filename != null) {
+                Log.v(TAG, "Got filename: " + filename);
+                SDLActivity.onNativeDropFile(filename);
+            }
+        }
+    }
+
+    protected void pauseNativeThread() {
+        mNextNativeState = NativeState.PAUSED;
+        mIsResumedCalled = false;
+
+        if (SDLActivity.mBrokenLibraries) {
+            return;
+        }
+
+        if (mHIDDeviceManager != null) {
+            mHIDDeviceManager.setFrozen(true);
+        }
+
+        SDLActivity.handleNativeState();
+    }
+
+    protected void resumeNativeThread() {
+        mNextNativeState = NativeState.RESUMED;
+        mIsResumedCalled = true;
+
+        if (SDLActivity.mBrokenLibraries) {
+           return;
+        }
+
+        if (mHIDDeviceManager != null) {
+            mHIDDeviceManager.setFrozen(false);
+        }
+
+        SDLActivity.handleNativeState();
+    }
+
+    // Events
+    @Override
+    protected void onPause() {
+        Log.v(TAG, "onPause()");
+        super.onPause();
+        if (!mHasMultiWindow) {
+            pauseNativeThread();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        Log.v(TAG, "onResume()");
+        super.onResume();
+        if (!mHasMultiWindow) {
+            resumeNativeThread();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        Log.v(TAG, "onStop()");
+        super.onStop();
+        if (mHasMultiWindow) {
+            pauseNativeThread();
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        Log.v(TAG, "onStart()");
+        super.onStart();
+        if (mHasMultiWindow) {
+            resumeNativeThread();
+        }
+    }
+
+    public static int getCurrentOrientation() {
+        final Context context = SDLActivity.getContext();
+        final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+
+        int result = SDL_ORIENTATION_UNKNOWN;
+
+        switch (display.getRotation()) {
+            case Surface.ROTATION_0:
+                result = SDL_ORIENTATION_PORTRAIT;
+                break;
+
+            case Surface.ROTATION_90:
+                result = SDL_ORIENTATION_LANDSCAPE;
+                break;
+
+            case Surface.ROTATION_180:
+                result = SDL_ORIENTATION_PORTRAIT_FLIPPED;
+                break;
+
+            case Surface.ROTATION_270:
+                result = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
+                break;
+        }
+
+        return result;
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
+
+        if (SDLActivity.mBrokenLibraries) {
+           return;
+        }
+
+        mHasFocus = hasFocus;
+        if (hasFocus) {
+           mNextNativeState = NativeState.RESUMED;
+           SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded();
+
+           SDLActivity.handleNativeState();
+           nativeFocusChanged(true);
+
+        } else {
+           nativeFocusChanged(false);
+           if (!mHasMultiWindow) {
+               mNextNativeState = NativeState.PAUSED;
+               SDLActivity.handleNativeState();
+           }
+        }
+    }
+
+    @Override
+    public void onLowMemory() {
+        Log.v(TAG, "onLowMemory()");
+        super.onLowMemory();
+
+        if (SDLActivity.mBrokenLibraries) {
+           return;
+        }
+
+        SDLActivity.nativeLowMemory();
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.v(TAG, "onDestroy()");
+
+        if (mHIDDeviceManager != null) {
+            HIDDeviceManager.release(mHIDDeviceManager);
+            mHIDDeviceManager = null;
+        }
+
+        if (SDLActivity.mBrokenLibraries) {
+           super.onDestroy();
+           return;
+        }
+
+        if (SDLActivity.mSDLThread != null) {
+
+            // Send Quit event to "SDLThread" thread
+            SDLActivity.nativeSendQuit();
+
+            // Wait for "SDLThread" thread to end
+            try {
+                SDLActivity.mSDLThread.join();
+            } catch(Exception e) {
+                Log.v(TAG, "Problem stopping SDLThread: " + e);
+            }
+        }
+
+        SDLActivity.nativeQuit();
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onBackPressed() {
+        // Check if we want to block the back button in case of mouse right click.
+        //
+        // If we do, the normal hardware back button will no longer work and people have to use home,
+        // but the mouse right click will work.
+        //
+        String trapBack = SDLActivity.nativeGetHint("SDL_ANDROID_TRAP_BACK_BUTTON");
+        if ((trapBack != null) && trapBack.equals("1")) {
+            // Exit and let the mouse handler handle this button (if appropriate)
+            return;
+        }
+
+        // Default system back button behavior.
+        super.onBackPressed();
+    }
+
+    // Called by JNI from SDL.
+    public static void manualBackButton() {
+        mSingleton.pressBackButton();
+    }
+
+    // Used to get us onto the activity's main thread
+    public void pressBackButton() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                SDLActivity.this.superOnBackPressed();
+            }
+        });
+    }
+
+    // Used to access the system back behavior.
+    public void superOnBackPressed() {
+        super.onBackPressed();
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+
+        if (SDLActivity.mBrokenLibraries) {
+           return false;
+        }
+
+        int keyCode = event.getKeyCode();
+        // Ignore certain special keys so they're handled by Android
+        // Urho3D - also ignore the Home key
+        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
+            keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+            keyCode == KeyEvent.KEYCODE_HOME ||
+            keyCode == KeyEvent.KEYCODE_CAMERA ||
+            keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */
+            keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */
+            ) {
+            return false;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    /* Transition to next state */
+    public static void handleNativeState() {
+
+        if (mNextNativeState == mCurrentNativeState) {
+            // Already in same state, discard.
+            return;
+        }
+
+        // Try a transition to init state
+        if (mNextNativeState == NativeState.INIT) {
+
+            mCurrentNativeState = mNextNativeState;
+            return;
+        }
+
+        // Try a transition to paused state
+        if (mNextNativeState == NativeState.PAUSED) {
+            if (mSDLThread != null) {
+                nativePause();
+            }
+            if (mSurface != null) {
+                mSurface.handlePause();
+            }
+            mCurrentNativeState = mNextNativeState;
+            return;
+        }
+
+        // Try a transition to resumed state
+        if (mNextNativeState == NativeState.RESUMED) {
+            if (mSurface.mIsSurfaceReady && mHasFocus && mIsResumedCalled) {
+                if (mSDLThread == null) {
+                    // This is the entry point to the C app.
+                    // Start up the C app thread and enable sensor input for the first time
+                    // FIXME: Why aren't we enabling sensor input at start?
+
+                    mSDLThread = new Thread(new SDLMain(), "SDLThread");
+                    mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+                    mSDLThread.start();
+
+                    // No nativeResume(), don't signal Android_ResumeSem
+                    mSurface.handleResume();
+                } else {
+                    nativeResume();
+                    mSurface.handleResume();
+                }
+
+                mCurrentNativeState = mNextNativeState;
+            }
+        }
+    }
+
+    // Messages from the SDLMain thread
+    static final int COMMAND_CHANGE_TITLE = 1;
+    static final int COMMAND_CHANGE_WINDOW_STYLE = 2;
+    static final int COMMAND_TEXTEDIT_HIDE = 3;
+    static final int COMMAND_CHANGE_SURFACEVIEW_FORMAT = 4;
+    static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
+
+    protected static final int COMMAND_USER = 0x8000;
+
+    protected static boolean mFullscreenModeActive;
+
+    /**
+     * This method is called by SDL if SDL did not handle a message itself.
+     * This happens if a received message contains an unsupported command.
+     * Method can be overwritten to handle Messages in a different class.
+     * @param command the command of the message.
+     * @param param the parameter of the message. May be null.
+     * @return if the message was handled in overridden method.
+     */
+    protected boolean onUnhandledMessage(int command, Object param) {
+        return false;
+    }
+
+    /**
+     * A Handler class for Messages from native SDL applications.
+     * It uses current Activities as target (e.g. for the title).
+     * static to prevent implicit references to enclosing object.
+     */
+    protected static class SDLCommandHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            Context context = SDL.getContext();
+            if (context == null) {
+                Log.e(TAG, "error handling message, getContext() returned null");
+                return;
+            }
+            switch (msg.arg1) {
+            case COMMAND_CHANGE_TITLE:
+                if (context instanceof Activity) {
+                    ((Activity) context).setTitle((String)msg.obj);
+                } else {
+                    Log.e(TAG, "error handling message, getContext() returned no Activity");
+                }
+                break;
+            case COMMAND_CHANGE_WINDOW_STYLE:
+                if (Build.VERSION.SDK_INT < 19) {
+                    // This version of Android doesn't support the immersive fullscreen mode
+                    break;
+                }
+                if (context instanceof Activity) {
+                    Window window = ((Activity) context).getWindow();
+                    if (window != null) {
+                        if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
+                            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
+                                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
+                                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+                                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
+                            window.getDecorView().setSystemUiVisibility(flags);
+                            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                            window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+                            SDLActivity.mFullscreenModeActive = true;
+                        } else {
+                            int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE;
+                            window.getDecorView().setSystemUiVisibility(flags);
+                            window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+                            window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                            SDLActivity.mFullscreenModeActive = false;
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "error handling message, getContext() returned no Activity");
+                }
+                break;
+            case COMMAND_TEXTEDIT_HIDE:
+                if (mTextEdit != null) {
+                    // Note: On some devices setting view to GONE creates a flicker in landscape.
+                    // Setting the View's sizes to 0 is similar to GONE but without the flicker.
+                    // The sizes will be set to useful values when the keyboard is shown again.
+                    mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
+
+                    InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+                    imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
+
+                    mScreenKeyboardShown = false;
+                }
+                break;
+            case COMMAND_SET_KEEP_SCREEN_ON:
+            {
+                if (context instanceof Activity) {
+                    Window window = ((Activity) context).getWindow();
+                    if (window != null) {
+                        if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
+                            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                        } else {
+                            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                        }
+                    }
+                }
+                break;
+            }
+            case COMMAND_CHANGE_SURFACEVIEW_FORMAT:
+            {
+                int format = (Integer) msg.obj;
+                int pf;
+
+                if (SDLActivity.mSurface == null) {
+                    return;
+                }
+
+                SurfaceHolder holder = SDLActivity.mSurface.getHolder();
+                if (holder == null) {
+                    return;
+                }
+
+                if (format == 1) {
+                    pf = PixelFormat.RGBA_8888;
+                } else if (format == 2) {
+                    pf = PixelFormat.RGBX_8888;
+                } else {
+                    pf = PixelFormat.RGB_565;
+                }
+
+                holder.setFormat(pf);
+
+                break;
+            }
+            default:
+                if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
+                    Log.e(TAG, "error handling message, command is " + msg.arg1);
+                }
+            }
+        }
+    }
+
+    // Handler for the messages
+    Handler commandHandler = new SDLCommandHandler();
+
+    // Send a message from the SDLMain thread
+    boolean sendCommand(int command, Object data) {
+        Message msg = commandHandler.obtainMessage();
+        msg.arg1 = command;
+        msg.obj = data;
+        boolean result = commandHandler.sendMessage(msg);
+
+        if ((Build.VERSION.SDK_INT >= 19) && (command == COMMAND_CHANGE_WINDOW_STYLE)) {
+            // Ensure we don't return until the resize has actually happened,
+            // or 500ms have passed.
+
+            boolean bShouldWait = false;
+
+            if (data instanceof Integer) {
+                // Let's figure out if we're already laid out fullscreen or not.
+                Display display = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+                android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics();
+                display.getRealMetrics( realMetrics );
+
+                boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) &&
+                                             (realMetrics.heightPixels == mSurface.getHeight()));
+
+                if (((Integer)data).intValue() == 1) {
+                    // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going
+                    // to change size and should wait for surfaceChanged() before we return, so the size
+                    // is right back in native code.  If we're already laid out fullscreen, though, we're
+                    // not going to change size even if we change decor modes, so we shouldn't wait for
+                    // surfaceChanged() -- which may not even happen -- and should return immediately.
+                    bShouldWait = !bFullscreenLayout;
+                }
+                else {
+                    // If we're laid out fullscreen (even if the status bar and nav bar are present),
+                    // or are actively in fullscreen, we're going to change size and should wait for
+                    // surfaceChanged before we return, so the size is right back in native code.
+                    bShouldWait = bFullscreenLayout;
+                }
+            }
+
+            if (bShouldWait) {
+                // We'll wait for the surfaceChanged() method, which will notify us
+                // when called.  That way, we know our current size is really the
+                // size we need, instead of grabbing a size that's still got
+                // the navigation and/or status bars before they're hidden.
+                //
+                // We'll wait for up to half a second, because some devices
+                // take a surprisingly long time for the surface resize, but
+                // then we'll just give up and return.
+                //
+                synchronized(SDLActivity.getContext()) {
+                    try {
+                        SDLActivity.getContext().wait(500);
+                    }
+                    catch (InterruptedException ie) {
+                        ie.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    // C functions we call
+    public static native int nativeSetupJNI();
+    public static native int nativeRunMain(String library, String function, Object arguments);
+    public static native void nativeLowMemory();
+    public static native void nativeSendQuit();
+    public static native void nativeQuit();
+    public static native void nativePause();
+    public static native void nativeResume();
+    public static native void nativeFocusChanged(boolean hasFocus);
+    public static native void onNativeDropFile(String filename);
+    public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate);
+    public static native void onNativeResize();
+    public static native void onNativeKeyDown(int keycode);
+    public static native void onNativeKeyUp(int keycode);
+    public static native boolean onNativeSoftReturnKey();
+    public static native void onNativeKeyboardFocusLost();
+    public static native void onNativeMouse(int button, int action, float x, float y, boolean relative);
+    public static native void onNativeTouch(int touchDevId, int pointerFingerId,
+                                            int action, float x,
+                                            float y, float p);
+    public static native void onNativeAccel(float x, float y, float z);
+    public static native void onNativeClipboardChanged();
+    public static native void onNativeSurfaceCreated();
+    public static native void onNativeSurfaceChanged();
+    public static native void onNativeSurfaceDestroyed();
+    public static native String nativeGetHint(String name);
+    public static native void nativeSetenv(String name, String value);
+    public static native void onNativeOrientationChanged(int orientation);
+    public static native void nativeAddTouch(int touchId, String name);
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setActivityTitle(String title) {
+        // Called from SDLMain() thread and can't directly affect the view
+        return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void setWindowStyle(boolean fullscreen) {
+        // Called from SDLMain() thread and can't directly affect the view
+        mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     * This is a static method for JNI convenience, it calls a non-static method
+     * so that is can be overridden
+     */
+    public static void setOrientation(int w, int h, boolean resizable, String hint)
+    {
+        if (mSingleton != null) {
+            mSingleton.setOrientationBis(w, h, resizable, hint);
+        }
+    }
+
+    /**
+     * This can be overridden
+     */
+    public void setOrientationBis(int w, int h, boolean resizable, String hint)
+    {
+        int orientation = -1;
+
+        if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
+            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+        } else if (hint.contains("LandscapeRight")) {
+            orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        } else if (hint.contains("LandscapeLeft")) {
+            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+        } else if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
+            orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
+        } else if (hint.contains("Portrait")) {
+            orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+        } else if (hint.contains("PortraitUpsideDown")) {
+            orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+        }
+
+        /* no valid hint */
+        if (orientation == -1) {
+            if (resizable) {
+                /* no fixed orientation */
+            } else {
+                if (w > h) {
+                    orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+                } else {
+                    orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
+                }
+            }
+        }
+
+        Log.v("SDL", "setOrientation() orientation=" + orientation + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint);
+        if (orientation != -1) {
+            mSingleton.setRequestedOrientation(orientation);
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void minimizeWindow() {
+
+        if (mSingleton == null) {
+            return;
+        }
+
+        Intent startMain = new Intent(Intent.ACTION_MAIN);
+        startMain.addCategory(Intent.CATEGORY_HOME);
+        startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mSingleton.startActivity(startMain);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean shouldMinimizeOnFocusLoss() {
+/*
+        if (Build.VERSION.SDK_INT >= 24) {
+            if (mSingleton == null) {
+                return true;
+            }
+
+            if (mSingleton.isInMultiWindowMode()) {
+                return false;
+            }
+
+            if (mSingleton.isInPictureInPictureMode()) {
+                return false;
+            }
+        }
+
+        return true;
+*/
+        return false;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isScreenKeyboardShown()
+    {
+        if (mTextEdit == null) {
+            return false;
+        }
+
+        if (!mScreenKeyboardShown) {
+            return false;
+        }
+
+        InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+        return imm.isAcceptingText();
+
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean supportsRelativeMouse()
+    {
+        // ChromeOS doesn't provide relative mouse motion via the Android 7 APIs
+        if (isChromebook()) {
+            return false;
+        }
+
+        // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under
+        // Android 7 APIs, and simply returns no data under Android 8 APIs.
+        //
+        // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and
+        // thus SDK version 27.  If we are in DeX mode and not API 27 or higher, as a result,
+        // we should stick to relative mode.
+        //
+        if ((Build.VERSION.SDK_INT < 27) && isDeXMode()) {
+            return false;
+        }
+
+        return SDLActivity.getMotionListener().supportsRelativeMouse();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setRelativeMouseEnabled(boolean enabled)
+    {
+        if (enabled && !supportsRelativeMouse()) {
+            return false;
+        }
+
+        return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean sendMessage(int command, int param) {
+        if (mSingleton == null) {
+            return false;
+        }
+        return mSingleton.sendCommand(command, Integer.valueOf(param));
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static Context getContext() {
+        return SDL.getContext();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isAndroidTV() {
+        UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE);
+        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+            return true;
+        }
+        if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) {
+            return true;
+        }
+        if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isTablet() {
+        DisplayMetrics metrics = new DisplayMetrics();
+        Activity activity = (Activity)getContext();
+        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+        double dWidthInches = metrics.widthPixels / (double)metrics.xdpi;
+        double dHeightInches = metrics.heightPixels / (double)metrics.ydpi;
+
+        double dDiagonal = Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches));
+
+        // If our diagonal size is seven inches or greater, we consider ourselves a tablet.
+        return (dDiagonal >= 7.0);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isChromebook() {
+        return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isDeXMode() {
+        if (Build.VERSION.SDK_INT < 24) {
+            return false;
+        }
+        try {
+            final Configuration config = getContext().getResources().getConfiguration();
+            final Class configClass = config.getClass();
+            return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
+                    == configClass.getField("semDesktopModeEnabled").getInt(config);
+        } catch(Exception ignored) {
+            return false;
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static DisplayMetrics getDisplayDPI() {
+        return getContext().getResources().getDisplayMetrics();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean getManifestEnvironmentVariables() {
+        try {
+            ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
+            Bundle bundle = applicationInfo.metaData;
+            if (bundle == null) {
+                return false;
+            }
+            String prefix = "SDL_ENV.";
+            final int trimLength = prefix.length();
+            for (String key : bundle.keySet()) {
+                if (key.startsWith(prefix)) {
+                    String name = key.substring(trimLength);
+                    String value = bundle.get(key).toString();
+                    nativeSetenv(name, value);
+                }
+            }
+            /* environment variables set! */
+            return true;
+        } catch (Exception e) {
+           Log.v("SDL", "exception " + e.toString());
+        }
+        return false;
+    }
+
+    // This method is called by SDLControllerManager's API 26 Generic Motion Handler.
+    public static View getContentView()
+    {
+        return mSingleton.mLayout;
+    }
+
+    static class ShowTextInputTask implements Runnable {
+        /*
+         * This is used to regulate the pan&scan method to have some offset from
+         * the bottom edge of the input region and the top edge of an input
+         * method (soft keyboard)
+         */
+        static final int HEIGHT_PADDING = 15;
+
+        public int x, y, w, h;
+
+        public ShowTextInputTask(int x, int y, int w, int h) {
+            this.x = x;
+            this.y = y;
+            this.w = w;
+            this.h = h;
+
+            /* Minimum size of 1 pixel, so it takes focus. */
+            if (this.w <= 0) {
+                this.w = 1;
+            }
+            if (this.h + HEIGHT_PADDING <= 0) {
+                this.h = 1 - HEIGHT_PADDING;
+            }
+        }
+
+        @Override
+        public void run() {
+            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
+            params.leftMargin = x;
+            params.topMargin = y;
+
+            if (mTextEdit == null) {
+                mTextEdit = new DummyEdit(SDL.getContext());
+
+                mLayout.addView(mTextEdit, params);
+            } else {
+                mTextEdit.setLayoutParams(params);
+            }
+
+            mTextEdit.setVisibility(View.VISIBLE);
+            mTextEdit.requestFocus();
+
+            InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            imm.showSoftInput(mTextEdit, 0);
+
+            mScreenKeyboardShown = true;
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean showTextInput(int x, int y, int w, int h) {
+        // Transfer the task to the main thread as a Runnable
+        return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
+    }
+
+    public static boolean isTextInputEvent(KeyEvent event) {
+
+        // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT
+        if (event.isCtrlPressed()) {
+            return false;
+        }
+
+        return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static Surface getNativeSurface() {
+        if (SDLActivity.mSurface == null) {
+            return null;
+        }
+        return SDLActivity.mSurface.getNativeSurface();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void setSurfaceViewFormat(int format) {
+        mSingleton.sendCommand(COMMAND_CHANGE_SURFACEVIEW_FORMAT, format);
+        return;
+    }
+
+    // Input
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void initTouch() {
+        int[] ids = InputDevice.getDeviceIds();
+
+        for (int i = 0; i < ids.length; ++i) {
+            InputDevice device = InputDevice.getDevice(ids[i]);
+            if (device != null && (device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+                nativeAddTouch(device.getId(), device.getName());
+            }
+        }
+    }
+
+    // APK expansion files support
+
+    /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
+    private static Object expansionFile;
+
+    /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
+    private static Method expansionFileMethod;
+
+    /**
+     * This method is called by SDL using JNI.
+     * @return an InputStream on success or null if no expansion file was used.
+     * @throws IOException on errors. Message is set for the SDL error message.
+     */
+    public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
+        // Get a ZipResourceFile representing a merger of both the main and patch files
+        if (expansionFile == null) {
+            String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
+            if (mainHint == null) {
+                return null; // no expansion use if no main version was set
+            }
+            String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION");
+            if (patchHint == null) {
+                return null; // no expansion use if no patch version was set
+            }
+
+            Integer mainVersion;
+            Integer patchVersion;
+            try {
+                mainVersion = Integer.valueOf(mainHint);
+                patchVersion = Integer.valueOf(patchHint);
+            } catch (NumberFormatException ex) {
+                ex.printStackTrace();
+                throw new IOException("No valid file versions set for APK expansion files", ex);
+            }
+
+            try {
+                // To avoid direct dependency on Google APK expansion library that is
+                // not a part of Android SDK we access it using reflection
+                expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
+                    .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
+                    .invoke(null, SDL.getContext(), mainVersion, patchVersion);
+
+                expansionFileMethod = expansionFile.getClass()
+                    .getMethod("getInputStream", String.class);
+            } catch (Exception ex) {
+                ex.printStackTrace();
+                expansionFile = null;
+                expansionFileMethod = null;
+                throw new IOException("Could not access APK expansion support library", ex);
+            }
+        }
+
+        // Get an input stream for a known file inside the expansion file ZIPs
+        InputStream fileStream;
+        try {
+            fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
+        } catch (Exception ex) {
+            // calling "getInputStream" failed
+            ex.printStackTrace();
+            throw new IOException("Could not open stream from APK expansion file", ex);
+        }
+
+        if (fileStream == null) {
+            // calling "getInputStream" was successful but null was returned
+            throw new IOException("Could not find path in APK expansion file");
+        }
+
+        return fileStream;
+    }
+
+    // Messagebox
+
+    /** Result of current messagebox. Also used for blocking the calling thread. */
+    protected final int[] messageboxSelection = new int[1];
+
+    /** Id of current dialog. */
+    protected int dialogs = 0;
+
+    /**
+     * This method is called by SDL using JNI.
+     * Shows the messagebox from UI thread and block calling thread.
+     * buttonFlags, buttonIds and buttonTexts must have same length.
+     * @param buttonFlags array containing flags for every button.
+     * @param buttonIds array containing id for every button.
+     * @param buttonTexts array containing text for every button.
+     * @param colors null for default or array of length 5 containing colors.
+     * @return button id or -1.
+     */
+    public int messageboxShowMessageBox(
+            final int flags,
+            final String title,
+            final String message,
+            final int[] buttonFlags,
+            final int[] buttonIds,
+            final String[] buttonTexts,
+            final int[] colors) {
+
+        messageboxSelection[0] = -1;
+
+        // sanity checks
+
+        if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
+            return -1; // implementation broken
+        }
+
+        // collect arguments for Dialog
+
+        final Bundle args = new Bundle();
+        args.putInt("flags", flags);
+        args.putString("title", title);
+        args.putString("message", message);
+        args.putIntArray("buttonFlags", buttonFlags);
+        args.putIntArray("buttonIds", buttonIds);
+        args.putStringArray("buttonTexts", buttonTexts);
+        args.putIntArray("colors", colors);
+
+        // trigger Dialog creation on UI thread
+
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                showDialog(dialogs++, args);
+            }
+        });
+
+        // block the calling thread
+
+        synchronized (messageboxSelection) {
+            try {
+                messageboxSelection.wait();
+            } catch (InterruptedException ex) {
+                ex.printStackTrace();
+                return -1;
+            }
+        }
+
+        // return selected value
+
+        return messageboxSelection[0];
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int ignore, Bundle args) {
+
+        // TODO set values from "flags" to messagebox dialog
+
+        // get colors
+
+        int[] colors = args.getIntArray("colors");
+        int backgroundColor;
+        int textColor;
+        int buttonBorderColor;
+        int buttonBackgroundColor;
+        int buttonSelectedColor;
+        if (colors != null) {
+            int i = -1;
+            backgroundColor = colors[++i];
+            textColor = colors[++i];
+            buttonBorderColor = colors[++i];
+            buttonBackgroundColor = colors[++i];
+            buttonSelectedColor = colors[++i];
+        } else {
+            backgroundColor = Color.TRANSPARENT;
+            textColor = Color.TRANSPARENT;
+            buttonBorderColor = Color.TRANSPARENT;
+            buttonBackgroundColor = Color.TRANSPARENT;
+            buttonSelectedColor = Color.TRANSPARENT;
+        }
+
+        // create dialog with title and a listener to wake up calling thread
+
+        final Dialog dialog = new Dialog(this);
+        dialog.setTitle(args.getString("title"));
+        dialog.setCancelable(false);
+        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+            @Override
+            public void onDismiss(DialogInterface unused) {
+                synchronized (messageboxSelection) {
+                    messageboxSelection.notify();
+                }
+            }
+        });
+
+        // create text
+
+        TextView message = new TextView(this);
+        message.setGravity(Gravity.CENTER);
+        message.setText(args.getString("message"));
+        if (textColor != Color.TRANSPARENT) {
+            message.setTextColor(textColor);
+        }
+
+        // create buttons
+
+        int[] buttonFlags = args.getIntArray("buttonFlags");
+        int[] buttonIds = args.getIntArray("buttonIds");
+        String[] buttonTexts = args.getStringArray("buttonTexts");
+
+        final SparseArray<Button> mapping = new SparseArray<Button>();
+
+        LinearLayout buttons = new LinearLayout(this);
+        buttons.setOrientation(LinearLayout.HORIZONTAL);
+        buttons.setGravity(Gravity.CENTER);
+        for (int i = 0; i < buttonTexts.length; ++i) {
+            Button button = new Button(this);
+            final int id = buttonIds[i];
+            button.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    messageboxSelection[0] = id;
+                    dialog.dismiss();
+                }
+            });
+            if (buttonFlags[i] != 0) {
+                // see SDL_messagebox.h
+                if ((buttonFlags[i] & 0x00000001) != 0) {
+                    mapping.put(KeyEvent.KEYCODE_ENTER, button);
+                }
+                if ((buttonFlags[i] & 0x00000002) != 0) {
+                    mapping.put(KeyEvent.KEYCODE_ESCAPE, button); /* API 11 */
+                }
+            }
+            button.setText(buttonTexts[i]);
+            if (textColor != Color.TRANSPARENT) {
+                button.setTextColor(textColor);
+            }
+            if (buttonBorderColor != Color.TRANSPARENT) {
+                // TODO set color for border of messagebox button
+            }
+            if (buttonBackgroundColor != Color.TRANSPARENT) {
+                Drawable drawable = button.getBackground();
+                if (drawable == null) {
+                    // setting the color this way removes the style
+                    button.setBackgroundColor(buttonBackgroundColor);
+                } else {
+                    // setting the color this way keeps the style (gradient, padding, etc.)
+                    drawable.setColorFilter(buttonBackgroundColor, PorterDuff.Mode.MULTIPLY);
+                }
+            }
+            if (buttonSelectedColor != Color.TRANSPARENT) {
+                // TODO set color for selected messagebox button
+            }
+            buttons.addView(button);
+        }
+
+        // create content
+
+        LinearLayout content = new LinearLayout(this);
+        content.setOrientation(LinearLayout.VERTICAL);
+        content.addView(message);
+        content.addView(buttons);
+        if (backgroundColor != Color.TRANSPARENT) {
+            content.setBackgroundColor(backgroundColor);
+        }
+
+        // add content to dialog and return
+
+        dialog.setContentView(content);
+        dialog.setOnKeyListener(new Dialog.OnKeyListener() {
+            @Override
+            public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) {
+                Button button = mapping.get(keyCode);
+                if (button != null) {
+                    if (event.getAction() == KeyEvent.ACTION_UP) {
+                        button.performClick();
+                    }
+                    return true; // also for ignored actions
+                }
+                return false;
+            }
+        });
+
+        return dialog;
+    }
+
+    private final Runnable rehideSystemUi = new Runnable() {
+        @Override
+        public void run() {
+            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
+                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
+                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
+
+            SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags);
+        }
+    };
+
+    public void onSystemUiVisibilityChange(int visibility) {
+        if (SDLActivity.mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)) {
+
+            Handler handler = getWindow().getDecorView().getHandler();
+            if (handler != null) {
+                handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop.
+                handler.postDelayed(rehideSystemUi, 2000);
+            }
+
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean clipboardHasText() {
+        return mClipboardHandler.clipboardHasText();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static String clipboardGetText() {
+        return mClipboardHandler.clipboardGetText();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void clipboardSetText(String string) {
+        mClipboardHandler.clipboardSetText(string);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) {
+        Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
+        ++mLastCursorID;
+
+        if (Build.VERSION.SDK_INT >= 24) {
+            try {
+                mCursors.put(mLastCursorID, PointerIcon.create(bitmap, hotSpotX, hotSpotY));
+            } catch (Exception e) {
+                return 0;
+            }
+        } else {
+            return 0;
+        }
+        return mLastCursorID;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setCustomCursor(int cursorID) {
+
+        if (Build.VERSION.SDK_INT >= 24) {
+            try {
+                mSurface.setPointerIcon(mCursors.get(cursorID));
+            } catch (Exception e) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setSystemCursor(int cursorID) {
+        int cursor_type = 0; //PointerIcon.TYPE_NULL;
+        switch (cursorID) {
+        case SDL_SYSTEM_CURSOR_ARROW:
+            cursor_type = 1000; //PointerIcon.TYPE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_IBEAM:
+            cursor_type = 1008; //PointerIcon.TYPE_TEXT;
+            break;
+        case SDL_SYSTEM_CURSOR_WAIT:
+            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
+            break;
+        case SDL_SYSTEM_CURSOR_CROSSHAIR:
+            cursor_type = 1007; //PointerIcon.TYPE_CROSSHAIR;
+            break;
+        case SDL_SYSTEM_CURSOR_WAITARROW:
+            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENWSE:
+            cursor_type = 1017; //PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENESW:
+            cursor_type = 1016; //PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZEWE:
+            cursor_type = 1014; //PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENS:
+            cursor_type = 1015; //PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZEALL:
+            cursor_type = 1020; //PointerIcon.TYPE_GRAB;
+            break;
+        case SDL_SYSTEM_CURSOR_NO:
+            cursor_type = 1012; //PointerIcon.TYPE_NO_DROP;
+            break;
+        case SDL_SYSTEM_CURSOR_HAND:
+            cursor_type = 1002; //PointerIcon.TYPE_HAND;
+            break;
+        }
+        if (Build.VERSION.SDK_INT >= 24) {
+            try {
+                mSurface.setPointerIcon(PointerIcon.getSystemIcon(SDL.getContext(), cursor_type));
+            } catch (Exception e) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+
+/**
+    Simple runnable to start the SDL application
+*/
+class SDLMain implements Runnable {
+    @Override
+    public void run() {
+        // Runs SDL_main()
+        String library = SDLActivity.mSingleton.getMainSharedObject();
+        String function = SDLActivity.mSingleton.getMainFunction();
+        String[] arguments = SDLActivity.mSingleton.getArguments();
+
+        try {
+            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY);
+        } catch (Exception e) {
+            Log.v("SDL", "modify thread properties failed " + e.toString());
+        }
+
+        Log.v("SDL", "Running main function " + function + " from library " + library);
+
+        SDLActivity.nativeRunMain(library, function, arguments);
+
+        Log.v("SDL", "Finished main function");
+
+        if (SDLActivity.mSingleton.isFinishing()) {
+            // Activity is already being destroyed
+        } else {
+            // Let's finish the Activity
+            SDLActivity.mSDLThread = null;
+            SDLActivity.mSingleton.finish();
+        }
+    }
+}
+
+
+/**
+    SDLSurface. This is what we draw on, so we need to know when it's created
+    in order to do anything useful.
+
+    Because of this, that's where we set up the SDL thread
+*/
+class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
+    View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
+
+    // Sensors
+    protected SensorManager mSensorManager;
+    protected Display mDisplay;
+
+    // Keep track of the surface size to normalize touch events
+    protected float mWidth, mHeight;
+
+    // Is SurfaceView ready for rendering
+    public boolean mIsSurfaceReady;
+
+    // Startup
+    public SDLSurface(Context context) {
+        super(context);
+        getHolder().addCallback(this);
+
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        requestFocus();
+        setOnKeyListener(this);
+        setOnTouchListener(this);
+
+        mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+
+        setOnGenericMotionListener(SDLActivity.getMotionListener());
+
+        // Some arbitrary defaults to avoid a potential division by zero
+        mWidth = 1.0f;
+        mHeight = 1.0f;
+
+        mIsSurfaceReady = false;
+    }
+
+    public void handlePause() {
+        enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+    }
+
+    public void handleResume() {
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        requestFocus();
+        setOnKeyListener(this);
+        setOnTouchListener(this);
+        enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+    }
+
+    public Surface getNativeSurface() {
+        return getHolder().getSurface();
+    }
+
+    // Called when we have a valid drawing surface
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v("SDL", "surfaceCreated()");
+        SDLActivity.onNativeSurfaceCreated();
+    }
+
+    // Called when we lose the surface
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.v("SDL", "surfaceDestroyed()");
+
+        // Transition to pause, if needed
+        SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
+        SDLActivity.handleNativeState();
+
+        mIsSurfaceReady = false;
+        SDLActivity.onNativeSurfaceDestroyed();
+    }
+
+    // Called when the surface is resized
+    @Override
+    public void surfaceChanged(SurfaceHolder holder,
+                               int format, int width, int height) {
+        Log.v("SDL", "surfaceChanged()");
+
+        if (SDLActivity.mSingleton == null) {
+            return;
+        }
+
+        int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
+        switch (format) {
+        case PixelFormat.RGBA_8888:
+            Log.v("SDL", "pixel format RGBA_8888");
+            sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
+            break;
+        case PixelFormat.RGBX_8888:
+            Log.v("SDL", "pixel format RGBX_8888");
+            sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
+            break;
+        case PixelFormat.RGB_565:
+            Log.v("SDL", "pixel format RGB_565");
+            sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
+            break;
+        case PixelFormat.RGB_888:
+            Log.v("SDL", "pixel format RGB_888");
+            // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
+            sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
+            break;
+        default:
+            Log.v("SDL", "pixel format unknown " + format);
+            break;
+        }
+
+        mWidth = width;
+        mHeight = height;
+        int nDeviceWidth = width;
+        int nDeviceHeight = height;
+        try
+        {
+            if (Build.VERSION.SDK_INT >= 17) {
+                android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics();
+                mDisplay.getRealMetrics( realMetrics );
+                nDeviceWidth = realMetrics.widthPixels;
+                nDeviceHeight = realMetrics.heightPixels;
+            }
+        }
+        catch ( java.lang.Throwable throwable ) {}
+
+        synchronized(SDLActivity.getContext()) {
+            // In case we're waiting on a size change after going fullscreen, send a notification.
+            SDLActivity.getContext().notifyAll();
+        }
+
+        Log.v("SDL", "Window size: " + width + "x" + height);
+        Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
+        SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, sdlFormat, mDisplay.getRefreshRate());
+        SDLActivity.onNativeResize();
+
+        // Prevent a screen distortion glitch,
+        // for instance when the device is in Landscape and a Portrait App is resumed.
+        boolean skip = false;
+        int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
+
+        if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
+        {
+            // Accept any
+        }
+        else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT)
+        {
+            if (mWidth > mHeight) {
+               skip = true;
+            }
+        } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
+            if (mWidth < mHeight) {
+               skip = true;
+            }
+        }
+
+        // Special Patch for Square Resolution: Black Berry Passport
+        if (skip) {
+           double min = Math.min(mWidth, mHeight);
+           double max = Math.max(mWidth, mHeight);
+
+           if (max / min < 1.20) {
+              Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
+              skip = false;
+           }
+        }
+
+        // Don't skip in MultiWindow.
+        if (skip) {
+            if (Build.VERSION.SDK_INT >= 24) {
+                if (SDLActivity.mSingleton.isInMultiWindowMode()) {
+                    Log.v("SDL", "Don't skip in Multi-Window");
+                    skip = false;
+                }
+            }
+        }
+
+        if (skip) {
+           Log.v("SDL", "Skip .. Surface is not ready.");
+           mIsSurfaceReady = false;
+           return;
+        }
+
+        /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
+        SDLActivity.onNativeSurfaceChanged();
+
+        /* Surface is ready */
+        mIsSurfaceReady = true;
+
+        SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
+        SDLActivity.handleNativeState();
+    }
+
+    // Key events
+    @Override
+    public boolean onKey(View  v, int keyCode, KeyEvent event) {
+
+        int deviceId = event.getDeviceId();
+        int source = event.getSource();
+
+        // Dispatch the different events depending on where they come from
+        // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
+        // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
+        //
+        // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
+        // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
+        // So, retrieve the device itself and check all of its sources
+        if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
+            // Note that we process events with specific key codes here
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
+                    return true;
+                }
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
+                    return true;
+                }
+            }
+        }
+
+        if (source == InputDevice.SOURCE_UNKNOWN) {
+            InputDevice device = InputDevice.getDevice(deviceId);
+            source = device.getSources();
+        }
+
+        if ((source & InputDevice.SOURCE_KEYBOARD) != 0) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                //Log.v("SDL", "key down: " + keyCode);
+                if (SDLActivity.isTextInputEvent(event)) {
+                    SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
+                }
+                SDLActivity.onNativeKeyDown(keyCode);
+                return true;
+            }
+            else if (event.getAction() == KeyEvent.ACTION_UP) {
+                //Log.v("SDL", "key up: " + keyCode);
+                SDLActivity.onNativeKeyUp(keyCode);
+                return true;
+            }
+        }
+
+        if ((source & InputDevice.SOURCE_MOUSE) != 0) {
+            // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
+            // they are ignored here because sending them as mouse input to SDL is messy
+            if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
+                switch (event.getAction()) {
+                case KeyEvent.ACTION_DOWN:
+                case KeyEvent.ACTION_UP:
+                    // mark the event as handled or it will be handled by system
+                    // handling KEYCODE_BACK by system will call onBackPressed()
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    // Touch events
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        /* Ref: http://developer.android.com/training/gestures/multi.html */
+        final int touchDevId = event.getDeviceId();
+        final int pointerCount = event.getPointerCount();
+        int action = event.getActionMasked();
+        int pointerFingerId;
+        int mouseButton;
+        int i = -1;
+        float x,y,p;
+
+        // 12290 = Samsung DeX mode desktop mouse
+        // 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
+        // 0x2   = SOURCE_CLASS_POINTER
+        if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
+            try {
+                mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
+            } catch(Exception e) {
+                mouseButton = 1;    // oh well.
+            }
+
+            // We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
+            // if we are.  We'll leverage our existing mouse motion listener
+            SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
+            x = motionListener.getEventX(event);
+            y = motionListener.getEventY(event);
+
+            SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
+        } else {
+            switch(action) {
+                case MotionEvent.ACTION_MOVE:
+                    for (i = 0; i < pointerCount; i++) {
+                        pointerFingerId = event.getPointerId(i);
+                        x = event.getX(i) / mWidth;
+                        y = event.getY(i) / mHeight;
+                        p = event.getPressure(i);
+                        if (p > 1.0f) {
+                            // may be larger than 1.0f on some devices
+                            // see the documentation of getPressure(i)
+                            p = 1.0f;
+                        }
+                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+                    }
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_DOWN:
+                    // Primary pointer up/down, the index is always zero
+                    i = 0;
+                case MotionEvent.ACTION_POINTER_UP:
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    // Non primary pointer up/down
+                    if (i == -1) {
+                        i = event.getActionIndex();
+                    }
+
+                    pointerFingerId = event.getPointerId(i);
+                    x = event.getX(i) / mWidth;
+                    y = event.getY(i) / mHeight;
+                    p = event.getPressure(i);
+                    if (p > 1.0f) {
+                        // may be larger than 1.0f on some devices
+                        // see the documentation of getPressure(i)
+                        p = 1.0f;
+                    }
+                    SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+                    break;
+
+                case MotionEvent.ACTION_CANCEL:
+                    for (i = 0; i < pointerCount; i++) {
+                        pointerFingerId = event.getPointerId(i);
+                        x = event.getX(i) / mWidth;
+                        y = event.getY(i) / mHeight;
+                        p = event.getPressure(i);
+                        if (p > 1.0f) {
+                            // may be larger than 1.0f on some devices
+                            // see the documentation of getPressure(i)
+                            p = 1.0f;
+                        }
+                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        return true;
+   }
+
+    // Sensor events
+    public void enableSensor(int sensortype, boolean enabled) {
+        // TODO: This uses getDefaultSensor - what if we have >1 accels?
+        if (enabled) {
+            mSensorManager.registerListener(this,
+                            mSensorManager.getDefaultSensor(sensortype),
+                            SensorManager.SENSOR_DELAY_GAME, null);
+        } else {
+            mSensorManager.unregisterListener(this,
+                            mSensorManager.getDefaultSensor(sensortype));
+        }
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // TODO
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+
+            // Since we may have an orientation set, we won't receive onConfigurationChanged events.
+            // We thus should check here.
+            int newOrientation = SDLActivity.SDL_ORIENTATION_UNKNOWN;
+
+            float x, y;
+            switch (mDisplay.getRotation()) {
+                case Surface.ROTATION_90:
+                    x = -event.values[1];
+                    y = event.values[0];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
+                    break;
+                case Surface.ROTATION_270:
+                    x = event.values[1];
+                    y = -event.values[0];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
+                    break;
+                case Surface.ROTATION_180:
+                    x = -event.values[0];
+                    y = -event.values[1];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
+                    break;
+                default:
+                    x = event.values[0];
+                    y = event.values[1];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
+                    break;
+            }
+
+            if (newOrientation != SDLActivity.mCurrentOrientation) {
+                SDLActivity.mCurrentOrientation = newOrientation;
+                SDLActivity.onNativeOrientationChanged(newOrientation);
+            }
+
+            SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
+                                      y / SensorManager.GRAVITY_EARTH,
+                                      event.values[2] / SensorManager.GRAVITY_EARTH);
+
+
+        }
+    }
+
+    // Captured pointer events for API 26.
+    public boolean onCapturedPointerEvent(MotionEvent event)
+    {
+        int action = event.getActionMasked();
+
+        float x, y;
+        switch (action) {
+            case MotionEvent.ACTION_SCROLL:
+                x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                SDLActivity.onNativeMouse(0, action, x, y, false);
+                return true;
+
+            case MotionEvent.ACTION_HOVER_MOVE:
+            case MotionEvent.ACTION_MOVE:
+                x = event.getX(0);
+                y = event.getY(0);
+                SDLActivity.onNativeMouse(0, action, x, y, true);
+                return true;
+
+            case MotionEvent.ACTION_BUTTON_PRESS:
+            case MotionEvent.ACTION_BUTTON_RELEASE:
+
+                // Change our action value to what SDL's code expects.
+                if (action == MotionEvent.ACTION_BUTTON_PRESS) {
+                    action = MotionEvent.ACTION_DOWN;
+                }
+                else if (action == MotionEvent.ACTION_BUTTON_RELEASE) {
+                    action = MotionEvent.ACTION_UP;
+                }
+
+                x = event.getX(0);
+                y = event.getY(0);
+                int button = event.getButtonState();
+
+                SDLActivity.onNativeMouse(button, action, x, y, true);
+                return true;
+        }
+
+        return false;
+    }
+
+}
+
+/* This is a fake invisible editor view that receives the input and defines the
+ * pan&scan region
+ */
+class DummyEdit extends View implements View.OnKeyListener {
+    InputConnection ic;
+
+    public DummyEdit(Context context) {
+        super(context);
+        setFocusableInTouchMode(true);
+        setFocusable(true);
+        setOnKeyListener(this);
+    }
+
+    @Override
+    public boolean onCheckIsTextEditor() {
+        return true;
+    }
+
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        /*
+         * This handles the hardware keyboard input
+         */
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            if (SDLActivity.isTextInputEvent(event)) {
+                ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
+                return true;
+            }
+            SDLActivity.onNativeKeyDown(keyCode);
+            return true;
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            SDLActivity.onNativeKeyUp(keyCode);
+            return true;
+        }
+        return false;
+    }
+
+    //
+    @Override
+    public boolean onKeyPreIme (int keyCode, KeyEvent event) {
+        // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
+        // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
+        // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
+        // FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
+        // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
+        // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
+        if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+            if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
+                SDLActivity.onNativeKeyboardFocusLost();
+            }
+        }
+        return super.onKeyPreIme(keyCode, event);
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        ic = new SDLInputConnection(this, true);
+
+        outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
+        outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+                | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
+
+        return ic;
+    }
+}
+
+class SDLInputConnection extends BaseInputConnection {
+
+    public SDLInputConnection(View targetView, boolean fullEditor) {
+        super(targetView, fullEditor);
+
+    }
+
+    @Override
+    public boolean sendKeyEvent(KeyEvent event) {
+        /*
+         * This used to handle the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
+         * However, as of Ice Cream Sandwich and later, almost all soft keyboard doesn't generate key presses
+         * and so we need to generate them ourselves in commitText.  To avoid duplicates on the handful of keys
+         * that still do, we empty this out.
+         */
+
+        /*
+         * Return DOES still generate a key event, however.  So rather than using it as the 'click a button' key
+         * as we do with physical keyboards, let's just use it to hide the keyboard.
+         */
+
+        if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
+            if (SDLActivity.onNativeSoftReturnKey()) {
+                return true;
+            }
+        }
+
+
+        return super.sendKeyEvent(event);
+    }
+
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+
+        for (int i = 0; i < text.length(); i++) {
+            char c = text.charAt(i);
+            if (c == '\n') {
+                if (SDLActivity.onNativeSoftReturnKey()) {
+                    return true;
+                }
+            }
+            nativeGenerateScancodeForUnichar(c);
+        }
+
+        SDLInputConnection.nativeCommitText(text.toString(), newCursorPosition);
+
+        return super.commitText(text, newCursorPosition);
+    }
+
+    @Override
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+
+        nativeSetComposingText(text.toString(), newCursorPosition);
+
+        return super.setComposingText(text, newCursorPosition);
+    }
+
+    public static native void nativeCommitText(String text, int newCursorPosition);
+
+    public native void nativeGenerateScancodeForUnichar(char c);
+
+    public native void nativeSetComposingText(String text, int newCursorPosition);
+
+    @Override
+    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+        // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
+        // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
+        if (beforeLength > 0 && afterLength == 0) {
+            boolean ret = true;
+            // backspace(s)
+            while (beforeLength-- > 0) {
+               boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
+                              && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
+               ret = ret && ret_key;
+            }
+            return ret;
+        }
+
+        return super.deleteSurroundingText(beforeLength, afterLength);
+    }
+}
+
+interface SDLClipboardHandler {
+
+    public boolean clipboardHasText();
+    public String clipboardGetText();
+    public void clipboardSetText(String string);
+
+}
+
+
+class SDLClipboardHandler_API11 implements
+    SDLClipboardHandler,
+    android.content.ClipboardManager.OnPrimaryClipChangedListener {
+
+    protected android.content.ClipboardManager mClipMgr;
+
+    SDLClipboardHandler_API11() {
+       mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+       mClipMgr.addPrimaryClipChangedListener(this);
+    }
+
+    @Override
+    public boolean clipboardHasText() {
+       return mClipMgr.hasText();
+    }
+
+    @Override
+    public String clipboardGetText() {
+        CharSequence text;
+        text = mClipMgr.getText();
+        if (text != null) {
+           return text.toString();
+        }
+        return null;
+    }
+
+    @Override
+    public void clipboardSetText(String string) {
+       mClipMgr.removePrimaryClipChangedListener(this);
+       mClipMgr.setText(string);
+       mClipMgr.addPrimaryClipChangedListener(this);
+    }
+
+    @Override
+    public void onPrimaryClipChanged() {
+        SDLActivity.onNativeClipboardChanged();
+    }
+
+}
+

+ 394 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java

@@ -0,0 +1,394 @@
+// Modified by Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import android.annotation.TargetApi;
+import android.media.*;
+import android.os.Build;
+import android.util.Log;
+
+public class SDLAudioManager
+{
+    protected static final String TAG = "SDLAudio";
+
+    protected static AudioTrack mAudioTrack;
+    protected static AudioRecord mAudioRecord;
+
+    public static void initialize() {
+        mAudioTrack = null;
+        mAudioRecord = null;
+    }
+
+    // Audio
+
+    protected static String getAudioFormatString(int audioFormat) {
+        switch (audioFormat) {
+        case AudioFormat.ENCODING_PCM_8BIT:
+            return "8-bit";
+        case AudioFormat.ENCODING_PCM_16BIT:
+            return "16-bit";
+        case AudioFormat.ENCODING_PCM_FLOAT:
+            return "float";
+        default:
+            return Integer.toString(audioFormat);
+        }
+    }
+
+    protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+        int channelConfig;
+        int sampleSize;
+        int frameSize;
+
+        Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
+
+        /* On older devices let's use known good settings */
+        if (Build.VERSION.SDK_INT < 21) {
+            if (desiredChannels > 2) {
+                desiredChannels = 2;
+            }
+            if (sampleRate < 8000) {
+                sampleRate = 8000;
+            } else if (sampleRate > 48000) {
+                sampleRate = 48000;
+            }
+        }
+
+        if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
+            int minSDKVersion = (isCapture ? 23 : 21);
+            if (Build.VERSION.SDK_INT < minSDKVersion) {
+                audioFormat = AudioFormat.ENCODING_PCM_16BIT;
+            }
+        }
+        switch (audioFormat)
+        {
+        case AudioFormat.ENCODING_PCM_8BIT:
+            sampleSize = 1;
+            break;
+        case AudioFormat.ENCODING_PCM_16BIT:
+            sampleSize = 2;
+            break;
+        case AudioFormat.ENCODING_PCM_FLOAT:
+            sampleSize = 4;
+            break;
+        default:
+            Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
+            audioFormat = AudioFormat.ENCODING_PCM_16BIT;
+            sampleSize = 2;
+            break;
+        }
+
+        if (isCapture) {
+            switch (desiredChannels) {
+            case 1:
+                channelConfig = AudioFormat.CHANNEL_IN_MONO;
+                break;
+            case 2:
+                channelConfig = AudioFormat.CHANNEL_IN_STEREO;
+                break;
+            default:
+                Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
+                desiredChannels = 2;
+                channelConfig = AudioFormat.CHANNEL_IN_STEREO;
+                break;
+            }
+        } else {
+            switch (desiredChannels) {
+            case 1:
+                channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+                break;
+            case 2:
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+                break;
+            case 3:
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
+                break;
+            case 4:
+                channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
+                break;
+            case 5:
+                channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
+                break;
+            case 6:
+                channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
+                break;
+            case 7:
+                channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
+                break;
+            case 8:
+                if (Build.VERSION.SDK_INT >= 23) {
+                    channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
+                } else {
+                    Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
+                    desiredChannels = 6;
+                    channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
+                }
+                break;
+            default:
+                Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
+                desiredChannels = 2;
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+                break;
+            }
+
+/*
+            Log.v(TAG, "Speaker configuration (and order of channels):");
+
+            if ((channelConfig & 0x00000004) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_LEFT");
+            }
+            if ((channelConfig & 0x00000008) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_RIGHT");
+            }
+            if ((channelConfig & 0x00000010) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_CENTER");
+            }
+            if ((channelConfig & 0x00000020) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_LOW_FREQUENCY");
+            }
+            if ((channelConfig & 0x00000040) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_BACK_LEFT");
+            }
+            if ((channelConfig & 0x00000080) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_BACK_RIGHT");
+            }
+            if ((channelConfig & 0x00000100) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
+            }
+            if ((channelConfig & 0x00000200) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
+            }
+            if ((channelConfig & 0x00000400) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_BACK_CENTER");
+            }
+            if ((channelConfig & 0x00000800) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_SIDE_LEFT");
+            }
+            if ((channelConfig & 0x00001000) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_SIDE_RIGHT");
+            }
+*/
+        }
+        frameSize = (sampleSize * desiredChannels);
+
+        // Let the user pick a larger buffer if they really want -- but ye
+        // gods they probably shouldn't, the minimums are horrifyingly high
+        // latency already
+        int minBufferSize;
+        if (isCapture) {
+            minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
+        } else {
+            minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
+        }
+        desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
+
+        int[] results = new int[4];
+
+        if (isCapture) {
+            if (mAudioRecord == null) {
+                mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
+                        channelConfig, audioFormat, desiredFrames * frameSize);
+
+                // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
+                if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                    Log.e(TAG, "Failed during initialization of AudioRecord");
+                    mAudioRecord.release();
+                    mAudioRecord = null;
+                    return null;
+                }
+
+                mAudioRecord.startRecording();
+            }
+
+            results[0] = mAudioRecord.getSampleRate();
+            results[1] = mAudioRecord.getAudioFormat();
+            results[2] = mAudioRecord.getChannelCount();
+            results[3] = desiredFrames;
+
+        } else {
+            if (mAudioTrack == null) {
+                mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+
+                // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
+                // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
+                // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
+                if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+                    /* Try again, with safer values */
+
+                    Log.e(TAG, "Failed during initialization of Audio Track");
+                    mAudioTrack.release();
+                    mAudioTrack = null;
+                    return null;
+                }
+
+                mAudioTrack.play();
+            }
+
+            results[0] = mAudioTrack.getSampleRate();
+            results[1] = mAudioTrack.getAudioFormat();
+            results[2] = mAudioTrack.getChannelCount();
+            results[3] = desiredFrames;
+        }
+
+        Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
+
+        return results;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+        return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    // Urho3D - suppress lint error
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static void audioWriteFloatBuffer(float[] buffer) {
+        if (mAudioTrack == null) {
+            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
+            return;
+        }
+
+        for (int i = 0; i < buffer.length;) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(1);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w(TAG, "SDL audio: error return from write(float)");
+                return;
+            }
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void audioWriteShortBuffer(short[] buffer) {
+        if (mAudioTrack == null) {
+            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
+            return;
+        }
+
+        for (int i = 0; i < buffer.length;) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(1);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w(TAG, "SDL audio: error return from write(short)");
+                return;
+            }
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void audioWriteByteBuffer(byte[] buffer) {
+        if (mAudioTrack == null) {
+            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
+            return;
+        }
+
+        for (int i = 0; i < buffer.length; ) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(1);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w(TAG, "SDL audio: error return from write(byte)");
+                return;
+            }
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+        return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
+    }
+
+    /** This method is called by SDL using JNI. */
+    // Urho3D - suppress lint error
+    @TargetApi(Build.VERSION_CODES.M)
+    public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
+        return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
+        if (Build.VERSION.SDK_INT < 23) {
+            return mAudioRecord.read(buffer, 0, buffer.length);
+        } else {
+            return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        }
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
+        if (Build.VERSION.SDK_INT < 23) {
+            return mAudioRecord.read(buffer, 0, buffer.length);
+        } else {
+            return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        }
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static void audioClose() {
+        if (mAudioTrack != null) {
+            mAudioTrack.stop();
+            mAudioTrack.release();
+            mAudioTrack = null;
+        }
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static void captureClose() {
+        if (mAudioRecord != null) {
+            mAudioRecord.stop();
+            mAudioRecord.release();
+            mAudioRecord = null;
+        }
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static void audioSetThreadPriority(boolean iscapture, int device_id) {
+        try {
+
+            /* Set thread name */
+            if (iscapture) {
+                Thread.currentThread().setName("SDLAudioC" + device_id);
+            } else {
+                Thread.currentThread().setName("SDLAudioP" + device_id);
+            }
+
+            /* Set thread priority */
+            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
+
+        } catch (Exception e) {
+            Log.v(TAG, "modify thread properties failed " + e.toString());
+        }
+    }
+
+    public static native int nativeSetupJNI();
+}

+ 799 - 0
Source/ThirdParty/SDL/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java

@@ -0,0 +1,799 @@
+// Modified by Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.*;
+import android.view.*;
+import android.util.Log;
+
+
+public class SDLControllerManager
+{
+
+    public static native int nativeSetupJNI();
+
+    public static native int nativeAddJoystick(int device_id, String name, String desc,
+                                               int vendor_id, int product_id,
+                                               boolean is_accelerometer, int button_mask,
+                                               int naxes, int nhats, int nballs);
+    public static native int nativeRemoveJoystick(int device_id);
+    public static native int nativeAddHaptic(int device_id, String name);
+    public static native int nativeRemoveHaptic(int device_id);
+    public static native int onNativePadDown(int device_id, int keycode);
+    public static native int onNativePadUp(int device_id, int keycode);
+    public static native void onNativeJoy(int device_id, int axis,
+                                          float value);
+    public static native void onNativeHat(int device_id, int hat_id,
+                                          int x, int y);
+
+    protected static SDLJoystickHandler mJoystickHandler;
+    protected static SDLHapticHandler mHapticHandler;
+
+    private static final String TAG = "SDLControllerManager";
+
+    public static void initialize() {
+        if (mJoystickHandler == null) {
+            if (Build.VERSION.SDK_INT >= 19) {
+                mJoystickHandler = new SDLJoystickHandler_API19();
+            } else {
+                mJoystickHandler = new SDLJoystickHandler_API16();
+            }
+        }
+
+        if (mHapticHandler == null) {
+            if (Build.VERSION.SDK_INT >= 26) {
+                mHapticHandler = new SDLHapticHandler_API26();
+            } else {
+                mHapticHandler = new SDLHapticHandler();
+            }
+        }
+    }
+
+    // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
+    public static boolean handleJoystickMotionEvent(MotionEvent event) {
+        return mJoystickHandler.handleMotionEvent(event);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void pollInputDevices() {
+        mJoystickHandler.pollInputDevices();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void pollHapticDevices() {
+        mHapticHandler.pollHapticDevices();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void hapticRun(int device_id, float intensity, int length) {
+        mHapticHandler.run(device_id, intensity, length);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void hapticStop(int device_id)
+    {
+        mHapticHandler.stop(device_id);
+    }
+
+    // Check if a given device is considered a possible SDL joystick
+    public static boolean isDeviceSDLJoystick(int deviceId) {
+        InputDevice device = InputDevice.getDevice(deviceId);
+        // We cannot use InputDevice.isVirtual before API 16, so let's accept
+        // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
+        if ((device == null) || (deviceId < 0)) {
+            return false;
+        }
+        int sources = device.getSources();
+
+        /* This is called for every button press, so let's not spam the logs */
+        /**
+        if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+            Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
+        }
+        if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
+            Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
+        }
+        if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
+            Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
+        }
+        **/
+
+        return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
+                ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
+                ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
+        );
+    }
+
+}
+
+class SDLJoystickHandler {
+
+    /**
+     * Handles given MotionEvent.
+     * @param event the event to be handled.
+     * @return if given event was processed.
+     */
+    public boolean handleMotionEvent(MotionEvent event) {
+        return false;
+    }
+
+    /**
+     * Handles adding and removing of input devices.
+     */
+    public void pollInputDevices() {
+    }
+}
+
+/* Actual joystick functionality available for API >= 12 devices */
+class SDLJoystickHandler_API16 extends SDLJoystickHandler {
+
+    static class SDLJoystick {
+        public int device_id;
+        public String name;
+        public String desc;
+        public ArrayList<InputDevice.MotionRange> axes;
+        public ArrayList<InputDevice.MotionRange> hats;
+    }
+    static class RangeComparator implements Comparator<InputDevice.MotionRange> {
+        @Override
+        public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
+            // Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
+            int arg0Axis = arg0.getAxis();
+            int arg1Axis = arg1.getAxis();
+            if (arg0Axis == MotionEvent.AXIS_GAS) {
+                arg0Axis = MotionEvent.AXIS_BRAKE;
+            } else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
+                arg0Axis = MotionEvent.AXIS_GAS;
+            }
+            if (arg1Axis == MotionEvent.AXIS_GAS) {
+                arg1Axis = MotionEvent.AXIS_BRAKE;
+            } else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
+                arg1Axis = MotionEvent.AXIS_GAS;
+            }
+
+            return arg0Axis - arg1Axis;
+        }
+    }
+
+    private ArrayList<SDLJoystick> mJoysticks;
+
+    public SDLJoystickHandler_API16() {
+
+        mJoysticks = new ArrayList<SDLJoystick>();
+    }
+
+    @Override
+    public void pollInputDevices() {
+        int[] deviceIds = InputDevice.getDeviceIds();
+        for(int i=0; i < deviceIds.length; ++i) {
+            SDLJoystick joystick = getJoystick(deviceIds[i]);
+            if (joystick == null) {
+                joystick = new SDLJoystick();
+                InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
+                if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
+                    joystick.device_id = deviceIds[i];
+                    joystick.name = joystickDevice.getName();
+                    joystick.desc = getJoystickDescriptor(joystickDevice);
+                    joystick.axes = new ArrayList<InputDevice.MotionRange>();
+                    joystick.hats = new ArrayList<InputDevice.MotionRange>();
+
+                    List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
+                    Collections.sort(ranges, new RangeComparator());
+                    for (InputDevice.MotionRange range : ranges ) {
+                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                            if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
+                                range.getAxis() == MotionEvent.AXIS_HAT_Y) {
+                                joystick.hats.add(range);
+                            }
+                            else {
+                                joystick.axes.add(range);
+                            }
+                        }
+                    }
+
+                    mJoysticks.add(joystick);
+                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
+                }
+            }
+        }
+
+        /* Check removed devices */
+        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
+        for(int i=0; i < mJoysticks.size(); i++) {
+            int device_id = mJoysticks.get(i).device_id;
+            int j;
+            for (j=0; j < deviceIds.length; j++) {
+                if (device_id == deviceIds[j]) break;
+            }
+            if (j == deviceIds.length) {
+                removedDevices.add(Integer.valueOf(device_id));
+            }
+        }
+
+        for(int i=0; i < removedDevices.size(); i++) {
+            int device_id = removedDevices.get(i).intValue();
+            SDLControllerManager.nativeRemoveJoystick(device_id);
+            for (int j=0; j < mJoysticks.size(); j++) {
+                if (mJoysticks.get(j).device_id == device_id) {
+                    mJoysticks.remove(j);
+                    break;
+                }
+            }
+        }
+    }
+
+    protected SDLJoystick getJoystick(int device_id) {
+        for(int i=0; i < mJoysticks.size(); i++) {
+            if (mJoysticks.get(i).device_id == device_id) {
+                return mJoysticks.get(i);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean handleMotionEvent(MotionEvent event) {
+        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
+            int actionPointerIndex = event.getActionIndex();
+            int action = event.getActionMasked();
+            switch(action) {
+                case MotionEvent.ACTION_MOVE:
+                    SDLJoystick joystick = getJoystick(event.getDeviceId());
+                    if ( joystick != null ) {
+                        for (int i = 0; i < joystick.axes.size(); i++) {
+                            InputDevice.MotionRange range = joystick.axes.get(i);
+                            /* Normalize the value to -1...1 */
+                            float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
+                            SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
+                        }
+                        for (int i = 0; i < joystick.hats.size(); i+=2) {
+                            int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
+                            int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
+                            SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        return true;
+    }
+
+    public String getJoystickDescriptor(InputDevice joystickDevice) {
+        String desc = joystickDevice.getDescriptor();
+
+        if (desc != null && !desc.isEmpty()) {
+            return desc;
+        }
+
+        return joystickDevice.getName();
+    }
+    public int getProductId(InputDevice joystickDevice) {
+        return 0;
+    }
+    public int getVendorId(InputDevice joystickDevice) {
+        return 0;
+    }
+    public int getButtonMask(InputDevice joystickDevice) {
+        return -1;
+    }
+}
+
+// Urho3D - suppress lint error
+@TargetApi(Build.VERSION_CODES.KITKAT)
+class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
+
+    @Override
+    public int getProductId(InputDevice joystickDevice) {
+        return joystickDevice.getProductId();
+    }
+
+    @Override
+    public int getVendorId(InputDevice joystickDevice) {
+        return joystickDevice.getVendorId();
+    }
+
+    @Override
+    public int getButtonMask(InputDevice joystickDevice) {
+        int button_mask = 0;
+        int[] keys = new int[] {
+            KeyEvent.KEYCODE_BUTTON_A,
+            KeyEvent.KEYCODE_BUTTON_B,
+            KeyEvent.KEYCODE_BUTTON_X,
+            KeyEvent.KEYCODE_BUTTON_Y,
+            KeyEvent.KEYCODE_BACK,
+            KeyEvent.KEYCODE_BUTTON_MODE,
+            KeyEvent.KEYCODE_BUTTON_START,
+            KeyEvent.KEYCODE_BUTTON_THUMBL,
+            KeyEvent.KEYCODE_BUTTON_THUMBR,
+            KeyEvent.KEYCODE_BUTTON_L1,
+            KeyEvent.KEYCODE_BUTTON_R1,
+            KeyEvent.KEYCODE_DPAD_UP,
+            KeyEvent.KEYCODE_DPAD_DOWN,
+            KeyEvent.KEYCODE_DPAD_LEFT,
+            KeyEvent.KEYCODE_DPAD_RIGHT,
+            KeyEvent.KEYCODE_BUTTON_SELECT,
+            KeyEvent.KEYCODE_DPAD_CENTER,
+
+            // These don't map into any SDL controller buttons directly
+            KeyEvent.KEYCODE_BUTTON_L2,
+            KeyEvent.KEYCODE_BUTTON_R2,
+            KeyEvent.KEYCODE_BUTTON_C,
+            KeyEvent.KEYCODE_BUTTON_Z,
+            KeyEvent.KEYCODE_BUTTON_1,
+            KeyEvent.KEYCODE_BUTTON_2,
+            KeyEvent.KEYCODE_BUTTON_3,
+            KeyEvent.KEYCODE_BUTTON_4,
+            KeyEvent.KEYCODE_BUTTON_5,
+            KeyEvent.KEYCODE_BUTTON_6,
+            KeyEvent.KEYCODE_BUTTON_7,
+            KeyEvent.KEYCODE_BUTTON_8,
+            KeyEvent.KEYCODE_BUTTON_9,
+            KeyEvent.KEYCODE_BUTTON_10,
+            KeyEvent.KEYCODE_BUTTON_11,
+            KeyEvent.KEYCODE_BUTTON_12,
+            KeyEvent.KEYCODE_BUTTON_13,
+            KeyEvent.KEYCODE_BUTTON_14,
+            KeyEvent.KEYCODE_BUTTON_15,
+            KeyEvent.KEYCODE_BUTTON_16,
+        };
+        int[] masks = new int[] {
+            (1 << 0),   // A -> A
+            (1 << 1),   // B -> B
+            (1 << 2),   // X -> X
+            (1 << 3),   // Y -> Y
+            (1 << 4),   // BACK -> BACK
+            (1 << 5),   // MODE -> GUIDE
+            (1 << 6),   // START -> START
+            (1 << 7),   // THUMBL -> LEFTSTICK
+            (1 << 8),   // THUMBR -> RIGHTSTICK
+            (1 << 9),   // L1 -> LEFTSHOULDER
+            (1 << 10),  // R1 -> RIGHTSHOULDER
+            (1 << 11),  // DPAD_UP -> DPAD_UP
+            (1 << 12),  // DPAD_DOWN -> DPAD_DOWN
+            (1 << 13),  // DPAD_LEFT -> DPAD_LEFT
+            (1 << 14),  // DPAD_RIGHT -> DPAD_RIGHT
+            (1 << 4),   // SELECT -> BACK
+            (1 << 0),   // DPAD_CENTER -> A
+            (1 << 15),  // L2 -> ??
+            (1 << 16),  // R2 -> ??
+            (1 << 17),  // C -> ??
+            (1 << 18),  // Z -> ??
+            (1 << 20),  // 1 -> ??
+            (1 << 21),  // 2 -> ??
+            (1 << 22),  // 3 -> ??
+            (1 << 23),  // 4 -> ??
+            (1 << 24),  // 5 -> ??
+            (1 << 25),  // 6 -> ??
+            (1 << 26),  // 7 -> ??
+            (1 << 27),  // 8 -> ??
+            (1 << 28),  // 9 -> ??
+            (1 << 29),  // 10 -> ??
+            (1 << 30),  // 11 -> ??
+            (1 << 31),  // 12 -> ??
+            // We're out of room...
+            0xFFFFFFFF,  // 13 -> ??
+            0xFFFFFFFF,  // 14 -> ??
+            0xFFFFFFFF,  // 15 -> ??
+            0xFFFFFFFF,  // 16 -> ??
+        };
+        boolean[] has_keys = joystickDevice.hasKeys(keys);
+        for (int i = 0; i < keys.length; ++i) {
+            if (has_keys[i]) {
+                button_mask |= masks[i];
+            }
+        }
+        return button_mask;
+    }
+}
+
+// Urho3D - suppress lint error
+@TargetApi(Build.VERSION_CODES.O)
+class SDLHapticHandler_API26 extends SDLHapticHandler {
+    @Override
+    public void run(int device_id, float intensity, int length) {
+        SDLHaptic haptic = getHaptic(device_id);
+        if (haptic != null) {
+            Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
+            if (intensity == 0.0f) {
+                stop(device_id);
+                return;
+            }
+
+            int vibeValue = Math.round(intensity * 255);
+
+            if (vibeValue > 255) {
+                vibeValue = 255;
+            }
+            if (vibeValue < 1) {
+                stop(device_id);
+                return;
+            }
+            try {
+                haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
+            }
+            catch (Exception e) {
+                // Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
+                // something went horribly wrong with the Android 8.0 APIs.
+                haptic.vib.vibrate(length);
+            }
+        }
+    }
+}
+
+class SDLHapticHandler {
+
+    class SDLHaptic {
+        public int device_id;
+        public String name;
+        public Vibrator vib;
+    }
+
+    private ArrayList<SDLHaptic> mHaptics;
+
+    public SDLHapticHandler() {
+        mHaptics = new ArrayList<SDLHaptic>();
+    }
+
+    public void run(int device_id, float intensity, int length) {
+        SDLHaptic haptic = getHaptic(device_id);
+        if (haptic != null) {
+            haptic.vib.vibrate(length);
+        }
+    }
+
+    public void stop(int device_id) {
+        SDLHaptic haptic = getHaptic(device_id);
+        if (haptic != null) {
+            haptic.vib.cancel();
+        }
+    }
+
+    public void pollHapticDevices() {
+
+        final int deviceId_VIBRATOR_SERVICE = 999999;
+        boolean hasVibratorService = false;
+
+        int[] deviceIds = InputDevice.getDeviceIds();
+        // It helps processing the device ids in reverse order
+        // For example, in the case of the XBox 360 wireless dongle,
+        // so the first controller seen by SDL matches what the receiver
+        // considers to be the first controller
+
+        for (int i = deviceIds.length - 1; i > -1; i--) {
+            SDLHaptic haptic = getHaptic(deviceIds[i]);
+            if (haptic == null) {
+                InputDevice device = InputDevice.getDevice(deviceIds[i]);
+                Vibrator vib = device.getVibrator();
+                if (vib.hasVibrator()) {
+                    haptic = new SDLHaptic();
+                    haptic.device_id = deviceIds[i];
+                    haptic.name = device.getName();
+                    haptic.vib = vib;
+                    mHaptics.add(haptic);
+                    SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
+                }
+            }
+        }
+
+        /* Check VIBRATOR_SERVICE */
+        Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
+        if (vib != null) {
+            hasVibratorService = vib.hasVibrator();
+
+            if (hasVibratorService) {
+                SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
+                if (haptic == null) {
+                    haptic = new SDLHaptic();
+                    haptic.device_id = deviceId_VIBRATOR_SERVICE;
+                    haptic.name = "VIBRATOR_SERVICE";
+                    haptic.vib = vib;
+                    mHaptics.add(haptic);
+                    SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
+                }
+            }
+        }
+
+        /* Check removed devices */
+        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
+        for(int i=0; i < mHaptics.size(); i++) {
+            int device_id = mHaptics.get(i).device_id;
+            int j;
+            for (j=0; j < deviceIds.length; j++) {
+                if (device_id == deviceIds[j]) break;
+            }
+
+            if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
+                // don't remove the vibrator if it is still present
+            } else if (j == deviceIds.length) {
+                removedDevices.add(device_id);
+            }
+        }
+
+        for(int i=0; i < removedDevices.size(); i++) {
+            int device_id = removedDevices.get(i);
+            SDLControllerManager.nativeRemoveHaptic(device_id);
+            for (int j=0; j < mHaptics.size(); j++) {
+                if (mHaptics.get(j).device_id == device_id) {
+                    mHaptics.remove(j);
+                    break;
+                }
+            }
+        }
+    }
+
+    protected SDLHaptic getHaptic(int device_id) {
+        for(int i=0; i < mHaptics.size(); i++) {
+            if (mHaptics.get(i).device_id == device_id) {
+                return mHaptics.get(i);
+            }
+        }
+        return null;
+    }
+}
+
+class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
+    // Generic Motion (mouse hover, joystick...) events go here
+    @Override
+    public boolean onGenericMotion(View v, MotionEvent event) {
+        float x, y;
+        int action;
+
+        switch ( event.getSource() ) {
+            case InputDevice.SOURCE_JOYSTICK:
+            case InputDevice.SOURCE_GAMEPAD:
+            case InputDevice.SOURCE_DPAD:
+                return SDLControllerManager.handleJoystickMotionEvent(event);
+
+            case InputDevice.SOURCE_MOUSE:
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        x = event.getX(0);
+                        y = event.getY(0);
+
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        // Event was not managed
+        return false;
+    }
+
+    public boolean supportsRelativeMouse() {
+        return false;
+    }
+
+    public boolean inRelativeMode() {
+        return false;
+    }
+
+    public boolean setRelativeMouseEnabled(boolean enabled) {
+        return false;
+    }
+
+    public void reclaimRelativeMouseModeIfNeeded()
+    {
+
+    }
+
+    public float getEventX(MotionEvent event) {
+        return event.getX(0);
+    }
+
+    public float getEventY(MotionEvent event) {
+        return event.getY(0);
+    }
+
+}
+
+// Urho3D - suppress lint error
+@TargetApi(Build.VERSION_CODES.N)
+class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
+    // Generic Motion (mouse hover, joystick...) events go here
+
+    private boolean mRelativeModeEnabled;
+
+    @Override
+    public boolean onGenericMotion(View v, MotionEvent event) {
+
+        // Handle relative mouse mode
+        if (mRelativeModeEnabled) {
+            if (event.getSource() == InputDevice.SOURCE_MOUSE) {
+                int action = event.getActionMasked();
+                if (action == MotionEvent.ACTION_HOVER_MOVE) {
+                    float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
+                    float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
+                    SDLActivity.onNativeMouse(0, action, x, y, true);
+                    return true;
+                }
+            }
+        }
+
+        // Event was not managed, call SDLGenericMotionListener_API12 method
+        return super.onGenericMotion(v, event);
+    }
+
+    @Override
+    public boolean supportsRelativeMouse() {
+        return true;
+    }
+
+    @Override
+    public boolean inRelativeMode() {
+        return mRelativeModeEnabled;
+    }
+
+    @Override
+    public boolean setRelativeMouseEnabled(boolean enabled) {
+        mRelativeModeEnabled = enabled;
+        return true;
+    }
+
+    @Override
+    public float getEventX(MotionEvent event) {
+        if (mRelativeModeEnabled) {
+            return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
+        }
+        else {
+            return event.getX(0);
+        }
+    }
+
+    @Override
+    public float getEventY(MotionEvent event) {
+        if (mRelativeModeEnabled) {
+            return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
+        }
+        else {
+            return event.getY(0);
+        }
+    }
+}
+
+
+// Urho3D - suppress lint error
+@TargetApi(Build.VERSION_CODES.O)
+class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
+    // Generic Motion (mouse hover, joystick...) events go here
+    private boolean mRelativeModeEnabled;
+
+    @Override
+    public boolean onGenericMotion(View v, MotionEvent event) {
+        float x, y;
+        int action;
+
+        switch ( event.getSource() ) {
+            case InputDevice.SOURCE_JOYSTICK:
+            case InputDevice.SOURCE_GAMEPAD:
+            case InputDevice.SOURCE_DPAD:
+                return SDLControllerManager.handleJoystickMotionEvent(event);
+
+            case InputDevice.SOURCE_MOUSE:
+            // DeX desktop mouse cursor is a separate non-standard input type.
+            case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        x = event.getX(0);
+                        y = event.getY(0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            case InputDevice.SOURCE_MOUSE_RELATIVE:
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        x = event.getX(0);
+                        y = event.getY(0);
+                        SDLActivity.onNativeMouse(0, action, x, y, true);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        // Event was not managed
+        return false;
+    }
+
+    @Override
+    public boolean supportsRelativeMouse() {
+        return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
+    }
+
+    @Override
+    public boolean inRelativeMode() {
+        return mRelativeModeEnabled;
+    }
+
+    @Override
+    public boolean setRelativeMouseEnabled(boolean enabled) {
+        if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
+            if (enabled) {
+                SDLActivity.getContentView().requestPointerCapture();
+            }
+            else {
+                SDLActivity.getContentView().releasePointerCapture();
+            }
+            mRelativeModeEnabled = enabled;
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    @Override
+    public void reclaimRelativeMouseModeIfNeeded()
+    {
+        if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
+            SDLActivity.getContentView().requestPointerCapture();
+        }
+    }
+
+    @Override
+    public float getEventX(MotionEvent event) {
+        // Relative mouse in capture mode will only have relative for X/Y
+        return event.getX(0);
+    }
+
+    @Override
+    public float getEventY(MotionEvent event) {
+        // Relative mouse in capture mode will only have relative for X/Y
+        return event.getY(0);
+    }
+}

+ 39 - 46
Source/ThirdParty/SDL/cmake/sdlchecks.cmake

@@ -397,6 +397,7 @@ macro(CheckLibSampleRate)
         else()
           get_soname (SAMPLERATE_LIB_SONAME SECRETRABBITCODE_LIBRARIES)
           set(SDL_LIBSAMPLERATE_DYNAMIC "\"${SAMPLERATE_LIB_SONAME}\"")
+          set(HAVE_LIBSAMPLERATE_SHARED TRUE)
         endif()
       else()
         list (APPEND EXTRA_LIBS samplerate)
@@ -421,7 +422,7 @@ macro(CheckX11)
       get_soname (${UPCASE_NAME}_LIB_SONAME ${UPCASE_NAME}_LIB)
     endforeach ()
 
-    # Urho3D - commented out setting of X_CFLAGS based on the search result for X11/Xlib.h using the default search path (if it is found then it is in default path anyway so no point to add it into compiler header search path again)
+    # Urho3D - commented out setting of EXTRA_CFLAGS based on the search result for X11/Xlib.h using the default search path (if it is found then it is in default path anyway so no point to add it into compiler header search path again)
     # Urho3D - add check for Xdbe extension
 
     check_include_file(X11/Xcursor/Xcursor.h HAVE_XCURSOR_H)
@@ -465,7 +466,7 @@ macro(CheckX11)
         endif()
         if(NOT HAVE_SHMAT)
           add_definitions(-DNO_SHARED_MEMORY)
-          set(X_CFLAGS "${X_CFLAGS} -DNO_SHARED_MEMORY")
+          list(APPEND EXTRA_CFLAGS "-DNO_SHARED_MEMORY")
         endif()
       endif()
 
@@ -485,8 +486,6 @@ macro(CheckX11)
         endif()
       endif()
 
-      set(SDL_CFLAGS "${SDL_CFLAGS} ${X_CFLAGS}")
-
       set(CMAKE_REQUIRED_LIBRARIES ${X11_LIB} ${X11_LIB})
       check_c_source_compiles("
           #include <X11/Xlib.h>
@@ -601,42 +600,6 @@ macro(CheckX11)
   endif()
 endmacro()
 
-# Requires:
-# - EGL
-# Optional:
-# - MIR_SHARED opt
-# - HAVE_DLOPEN opt
-macro(CheckMir)
-    if(VIDEO_MIR)
-        # Urho3D - bug fix - do not use pkg-config tool for detection as it only works for host environment and not for rooted environment when cross-compiling
-        find_package (Mir 0.26)
-        if (MIR_FOUND)
-            include_directories (SYSTEM ${MIR_INCLUDE_DIRS})
-            set(HAVE_VIDEO_MIR TRUE)
-            set(HAVE_SDL_VIDEO TRUE)
-
-            file(GLOB MIR_SOURCES ${SDL2_SOURCE_DIR}/src/video/mir/*.c)
-            set(SOURCE_FILES ${SOURCE_FILES} ${MIR_SOURCES})
-            set(SDL_VIDEO_DRIVER_MIR 1)
-            set(SDL_VIDEO_OPENGL_EGL 1)
-
-            if(MIR_SHARED)
-                if(NOT HAVE_DLOPEN)
-                    message_warn("You must have SDL_LoadObject() support for dynamic Mir loading")
-                else()
-                    get_soname (MIRCLIENT_LIB_SONAME MIR_CLIENT)
-                    get_soname (XKBCOMMON_LIB_SONAME XKB)
-                    set(SDL_VIDEO_DRIVER_MIR_DYNAMIC "\"${MIRCLIENT_LIB_SONAME}\"")
-                    set(SDL_VIDEO_DRIVER_MIR_DYNAMIC_XKBCOMMON "\"${XKBCOMMON_LIB_SONAME}\"")
-                    set(HAVE_MIR_SHARED TRUE)
-                endif()
-            else()
-                list (APPEND EXTRA_LIBS mirclient xkbcommon)
-            endif()
-        endif()
-    endif()
-endmacro()
-
 macro(WaylandProtocolGen _SCANNER _XML _PROTL)
     set(_WAYLAND_PROT_C_CODE "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols/${_PROTL}-protocol.c")
     set(_WAYLAND_PROT_H_CODE "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols/${_PROTL}-client-protocol.h")
@@ -655,7 +618,7 @@ macro(WaylandProtocolGen _SCANNER _XML _PROTL)
         ARGS code "${_XML}" "${_WAYLAND_PROT_C_CODE}"
     )
 
-    set(SOURCE_FILES ${SOURCE_FILES} "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols/${_PROTL}-protocol.c")
+    set(SOURCE_FILES ${SOURCE_FILES} "${_WAYLAND_PROT_C_CODE}")
 endmacro()
 
 # Requires:
@@ -679,11 +642,10 @@ macro(CheckWayland)
       file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols")
       include_directories("${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols")
 
-      WaylandProtocolGen("${WAYLAND_SCANNER}" "${WAYLAND_CORE_PROTOCOL_DIR}/wayland.xml" "wayland")
-
-      foreach(_PROTL relative-pointer-unstable-v1 pointer-constraints-unstable-v1)
-        string(REGEX REPLACE "\\-unstable\\-.*$" "" PROTSUBDIR ${_PROTL})
-        WaylandProtocolGen("${WAYLAND_SCANNER}" "${WAYLAND_PROTOCOLS_DIR}/unstable/${PROTSUBDIR}/${_PROTL}.xml" "${_PROTL}")
+      file(GLOB WAYLAND_PROTOCOLS_XML RELATIVE "${SDL2_SOURCE_DIR}/wayland-protocols/" "${SDL2_SOURCE_DIR}/wayland-protocols/*.xml")
+      foreach(_XML ${WAYLAND_PROTOCOLS_XML})
+        string(REGEX REPLACE "\\.xml$" "" _PROTL "${_XML}")
+        WaylandProtocolGen("${WAYLAND_SCANNER}" "${SDL2_SOURCE_DIR}/wayland-protocols/${_XML}" "${_PROTL}")
       endforeach()
 
       if(VIDEO_WAYLAND_QT_TOUCH)
@@ -1085,6 +1047,37 @@ macro(CheckUSBHID)
   endif()
 endmacro()
 
+# Check for HIDAPI joystick drivers. This is currently a Unix thing, not Windows or macOS!
+macro(CheckHIDAPI)
+  if(HIDAPI)
+    if(HIDAPI_SKIP_LIBUSB)
+      set(HAVE_HIDAPI TRUE)
+    else()
+      set(HAVE_HIDAPI FALSE)
+      pkg_check_modules(LIBUSB libusb)
+      if (LIBUSB_FOUND)
+        check_include_file(libusb.h HAVE_LIBUSB_H)
+        if (HAVE_LIBUSB_H)
+          set(HAVE_HIDAPI TRUE)
+        endif()
+      endif()
+    endif()
+
+    if(HAVE_HIDAPI)
+      set(SDL_JOYSTICK_HIDAPI 1)
+      set(HAVE_SDL_JOYSTICK TRUE)
+      file(GLOB HIDAPI_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/hidapi/*.c)
+      set(SOURCE_FILES ${SOURCE_FILES} ${HIDAPI_SOURCES})
+      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUSB_CFLAGS} -I${SDL2_SOURCE_DIR}/src/hidapi/hidapi")
+      if(NOT HIDAPI_SKIP_LIBUSB)
+        set(SOURCE_FILES ${SOURCE_FILES} ${SDL2_SOURCE_DIR}/src/hidapi/libusb/hid.c)
+        list(APPEND EXTRA_LIBS ${LIBUSB_LIBS})
+      endif()
+    endif()
+  endif()
+endmacro()
+
+
 # Requires:
 # - n/a
 macro(CheckRPI)

+ 4 - 2
Source/ThirdParty/SDL/include/SDL.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -51,6 +51,7 @@
 #include "SDL_power.h"
 #include "SDL_render.h"
 #include "SDL_rwops.h"
+#include "SDL_sensor.h"
 #include "SDL_shape.h"
 #include "SDL_system.h"
 #include "SDL_thread.h"
@@ -80,10 +81,11 @@ extern "C" {
 #define SDL_INIT_HAPTIC         0x00001000u
 #define SDL_INIT_GAMECONTROLLER 0x00002000u  /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
 #define SDL_INIT_EVENTS         0x00004000u
+#define SDL_INIT_SENSOR         0x00008000u
 #define SDL_INIT_NOPARACHUTE    0x00100000u  /**< compatibility; this flag is ignored. */
 #define SDL_INIT_EVERYTHING ( \
                 SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \
-                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \
+                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR \
             )
 /* @} */
 

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_assert.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 23 - 2
Source/ThirdParty/SDL/include/SDL_atomic.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -158,13 +158,33 @@ extern DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void);
 #if defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))
 #define SDL_MemoryBarrierRelease()   __asm__ __volatile__ ("lwsync" : : : "memory")
 #define SDL_MemoryBarrierAcquire()   __asm__ __volatile__ ("lwsync" : : : "memory")
+#elif defined(__GNUC__) && defined(__aarch64__)
+#define SDL_MemoryBarrierRelease()   __asm__ __volatile__ ("dmb ish" : : : "memory")
+#define SDL_MemoryBarrierAcquire()   __asm__ __volatile__ ("dmb ish" : : : "memory")
 #elif defined(__GNUC__) && defined(__arm__)
-#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)
+#if 0 /* defined(__LINUX__) || defined(__ANDROID__) */
+/* Information from:
+   https://chromium.googlesource.com/chromium/chromium/+/trunk/base/atomicops_internals_arm_gcc.h#19
+
+   The Linux kernel provides a helper function which provides the right code for a memory barrier,
+   hard-coded at address 0xffff0fa0
+*/
+typedef void (*SDL_KernelMemoryBarrierFunc)();
+#define SDL_MemoryBarrierRelease()	((SDL_KernelMemoryBarrierFunc)0xffff0fa0)()
+#define SDL_MemoryBarrierAcquire()	((SDL_KernelMemoryBarrierFunc)0xffff0fa0)()
+#elif 0 /* defined(__QNXNTO__) */
+#include <sys/cpuinline.h>
+
+#define SDL_MemoryBarrierRelease()   __cpu_membarrier()
+#define SDL_MemoryBarrierAcquire()   __cpu_membarrier()
+#else
+#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) || defined(__ARM_ARCH_8A__)
 #define SDL_MemoryBarrierRelease()   __asm__ __volatile__ ("dmb ish" : : : "memory")
 #define SDL_MemoryBarrierAcquire()   __asm__ __volatile__ ("dmb ish" : : : "memory")
 #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_5TE__)
 #ifdef __thumb__
 /* The mcr instruction isn't available in thumb mode, use real functions */
+#define SDL_MEMORY_BARRIER_USES_FUNCTION
 #define SDL_MemoryBarrierRelease()   SDL_MemoryBarrierReleaseFunction()
 #define SDL_MemoryBarrierAcquire()   SDL_MemoryBarrierAcquireFunction()
 #else
@@ -174,6 +194,7 @@ extern DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void);
 #else
 #define SDL_MemoryBarrierRelease()   __asm__ __volatile__ ("" : : : "memory")
 #define SDL_MemoryBarrierAcquire()   __asm__ __volatile__ ("" : : : "memory")
+#endif /* __LINUX__ || __ANDROID__ */
 #endif /* __GNUC__ && __arm__ */
 #else
 #if (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))

+ 50 - 16
Source/ThirdParty/SDL/include/SDL_audio.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -140,7 +140,8 @@ typedef Uint16 SDL_AudioFormat;
 #define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE    0x00000001
 #define SDL_AUDIO_ALLOW_FORMAT_CHANGE       0x00000002
 #define SDL_AUDIO_ALLOW_CHANNELS_CHANGE     0x00000004
-#define SDL_AUDIO_ALLOW_ANY_CHANGE          (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
+#define SDL_AUDIO_ALLOW_SAMPLES_CHANGE      0x00000008
+#define SDL_AUDIO_ALLOW_ANY_CHANGE          (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
 /* @} */
 
 /* @} *//* Audio flags */
@@ -419,23 +420,56 @@ extern DECLSPEC void SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev,
 /* @} *//* Pause audio functions */
 
 /**
- *  This function loads a WAVE from the data source, automatically freeing
- *  that source if \c freesrc is non-zero.  For example, to load a WAVE file,
- *  you could do:
+ *  \brief Load the audio data of a WAVE file into memory
+ *
+ *  Loading a WAVE file requires \c src, \c spec, \c audio_buf and \c audio_len
+ *  to be valid pointers. The entire data portion of the file is then loaded
+ *  into memory and decoded if necessary.
+ *
+ *  If \c freesrc is non-zero, the data source gets automatically closed and
+ *  freed before the function returns.
+ *
+ *  Supported are RIFF WAVE files with the formats PCM (8, 16, 24, and 32 bits),
+ *  IEEE Float (32 bits), Microsoft ADPCM and IMA ADPCM (4 bits), and A-law and
+ *  µ-law (8 bits). Other formats are currently unsupported and cause an error.
+ *
+ *  If this function succeeds, the pointer returned by it is equal to \c spec
+ *  and the pointer to the audio data allocated by the function is written to
+ *  \c audio_buf and its length in bytes to \c audio_len. The \ref SDL_AudioSpec
+ *  members \c freq, \c channels, and \c format are set to the values of the
+ *  audio data in the buffer. The \c samples member is set to a sane default and
+ *  all others are set to zero.
+ *
+ *  It's necessary to use SDL_FreeWAV() to free the audio data returned in
+ *  \c audio_buf when it is no longer used.
+ *
+ *  Because of the underspecification of the Waveform format, there are many
+ *  problematic files in the wild that cause issues with strict decoders. To
+ *  provide compatibility with these files, this decoder is lenient in regards
+ *  to the truncation of the file, the fact chunk, and the size of the RIFF
+ *  chunk. The hints SDL_HINT_WAVE_RIFF_CHUNK_SIZE, SDL_HINT_WAVE_TRUNCATION,
+ *  and SDL_HINT_WAVE_FACT_CHUNK can be used to tune the behavior of the
+ *  loading process.
+ *
+ *  Any file that is invalid (due to truncation, corruption, or wrong values in
+ *  the headers), too big, or unsupported causes an error. Additionally, any
+ *  critical I/O error from the data source will terminate the loading process
+ *  with an error. The function returns NULL on error and in all cases (with the
+ *  exception of \c src being NULL), an appropriate error message will be set.
+ *
+ *  It is required that the data source supports seeking.
+ *
+ *  Example:
  *  \code
  *      SDL_LoadWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, ...);
  *  \endcode
  *
- *  If this function succeeds, it returns the given SDL_AudioSpec,
- *  filled with the audio data format of the wave data, and sets
- *  \c *audio_buf to a malloc()'d buffer containing the audio data,
- *  and sets \c *audio_len to the length of that audio buffer, in bytes.
- *  You need to free the audio buffer with SDL_FreeWAV() when you are
- *  done with it.
- *
- *  This function returns NULL and sets the SDL error message if the
- *  wave file cannot be opened, uses an unknown data format, or is
- *  corrupt.  Currently raw and MS-ADPCM WAVE files are supported.
+ *  \param src The data source with the WAVE data
+ *  \param freesrc A integer value that makes the function close the data source if non-zero
+ *  \param spec A pointer filled with the audio format of the audio data
+ *  \param audio_buf A pointer filled with the audio data allocated by the function
+ *  \param audio_len A pointer filled with the length of the audio data buffer in bytes
+ *  \return NULL on error, or non-NULL on success.
  */
 extern DECLSPEC SDL_AudioSpec *SDLCALL SDL_LoadWAV_RW(SDL_RWops * src,
                                                       int freesrc,
@@ -527,7 +561,7 @@ extern DECLSPEC SDL_AudioStream * SDLCALL SDL_NewAudioStream(const SDL_AudioForm
  *
  *  \param stream The stream the audio data is being added to
  *  \param buf A pointer to the audio data to add
- *  \param int The number of bytes to write to the stream
+ *  \param len The number of bytes to write to the stream
  *  \return 0 on success, or -1 on error.
  *
  *  \sa SDL_NewAudioStream

+ 10 - 1
Source/ThirdParty/SDL/include/SDL_bits.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -101,6 +101,15 @@ SDL_MostSignificantBitIndex32(Uint32 x)
 #endif
 }
 
+SDL_FORCE_INLINE SDL_bool
+SDL_HasExactlyOneBitSet32(Uint32 x)
+{
+    if (x && !(x & (x - 1))) {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
+}
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }

+ 7 - 7
Source/ThirdParty/SDL/include/SDL_blendmode.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -90,12 +90,12 @@ typedef enum
 /**
  *  \brief Create a custom blend mode, which may or may not be supported by a given renderer
  *
- *  \param srcColorFactor
- *  \param dstColorFactor
- *  \param colorOperation
- *  \param srcAlphaFactor
- *  \param dstAlphaFactor
- *  \param alphaOperation
+ *  \param srcColorFactor source color factor
+ *  \param dstColorFactor destination color factor
+ *  \param colorOperation color operation
+ *  \param srcAlphaFactor source alpha factor
+ *  \param dstAlphaFactor destination alpha factor
+ *  \param alphaOperation alpha operation
  *
  *  The result of the blend mode operation will be:
  *      dstRGB = dstRGB * dstColorFactor colorOperation srcRGB * srcColorFactor

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_clipboard.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 39 - 10
Source/ThirdParty/SDL/include/SDL_config.h.cmake

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -132,19 +132,36 @@
 #cmakedefine HAVE_VSSCANF 1
 #cmakedefine HAVE_VSNPRINTF 1
 #cmakedefine HAVE_M_PI 1
-#cmakedefine HAVE_ATAN 1
-#cmakedefine HAVE_ATAN2 1
 #cmakedefine HAVE_ACOS 1
+#cmakedefine HAVE_ACOSF 1
 #cmakedefine HAVE_ASIN 1
+#cmakedefine HAVE_ASINF 1
+#cmakedefine HAVE_ATAN 1
+#cmakedefine HAVE_ATANF 1
+#cmakedefine HAVE_ATAN2 1
+#cmakedefine HAVE_ATAN2F 1
 #cmakedefine HAVE_CEIL 1
+#cmakedefine HAVE_CEILF 1
 #cmakedefine HAVE_COPYSIGN 1
+#cmakedefine HAVE_COPYSIGNF 1
 #cmakedefine HAVE_COS 1
 #cmakedefine HAVE_COSF 1
+#cmakedefine HAVE_EXP 1
+#cmakedefine HAVE_EXPF 1
 #cmakedefine HAVE_FABS 1
+#cmakedefine HAVE_FABSF 1
 #cmakedefine HAVE_FLOOR 1
+#cmakedefine HAVE_FLOORF 1
+#cmakedefine HAVE_FMOD 1
+#cmakedefine HAVE_FMODF 1
 #cmakedefine HAVE_LOG 1
+#cmakedefine HAVE_LOGF 1
+#cmakedefine HAVE_LOG10 1
+#cmakedefine HAVE_LOG10F 1
 #cmakedefine HAVE_POW 1
+#cmakedefine HAVE_POWF 1
 #cmakedefine HAVE_SCALBN 1
+#cmakedefine HAVE_SCALBNF 1
 #cmakedefine HAVE_SIN 1
 #cmakedefine HAVE_SINF 1
 #cmakedefine HAVE_SQRT 1
@@ -169,6 +186,7 @@
 #cmakedefine HAVE_SEM_TIMEDWAIT 1
 #cmakedefine HAVE_GETAUXVAL 1
 #cmakedefine HAVE_POLL 1
+#cmakedefine HAVE__EXIT 1
 
 #elif __WIN32__
 #cmakedefine HAVE_STDARG_H 1
@@ -180,20 +198,25 @@
 #endif /* HAVE_LIBC */
 
 #cmakedefine HAVE_ALTIVEC_H 1
-#cmakedefine HAVE_LIBUDEV_H 1
 #cmakedefine HAVE_DBUS_DBUS_H 1
-#cmakedefine HAVE_IBUS_IBUS_H 1
 #cmakedefine HAVE_FCITX_FRONTEND_H 1
+#cmakedefine HAVE_IBUS_IBUS_H 1
+#cmakedefine HAVE_IMMINTRIN_H 1
 #cmakedefine HAVE_LIBSAMPLERATE_H 1
+#cmakedefine HAVE_LIBUDEV_H 1
 
 #cmakedefine HAVE_D3D_H @HAVE_D3D_H@
 #cmakedefine HAVE_D3D11_H @HAVE_D3D11_H@
 #cmakedefine HAVE_DDRAW_H @HAVE_DDRAW_H@
 #cmakedefine HAVE_DSOUND_H @HAVE_DSOUND_H@
 #cmakedefine HAVE_DINPUT_H @HAVE_DINPUT_H@
-#cmakedefine HAVE_XAUDIO2_H @HAVE_XAUDIO2_H@
 #cmakedefine HAVE_XINPUT_H @HAVE_XINPUT_H@
 #cmakedefine HAVE_DXGI_H @HAVE_DXGI_H@
+
+#cmakedefine HAVE_ENDPOINTVOLUME_H @HAVE_ENDPOINTVOLUME_H@
+#cmakedefine HAVE_MMDEVICEAPI_H @HAVE_MMDEVICEAPI_H@
+#cmakedefine HAVE_AUDIOCLIENT_H @HAVE_AUDIOCLIENT_H@
+
 #cmakedefine HAVE_XINPUT_GAMEPAD_EX @HAVE_XINPUT_GAMEPAD_EX@
 #cmakedefine HAVE_XINPUT_STATE_EX @HAVE_XINPUT_STATE_EX@
 
@@ -208,6 +231,7 @@
 #cmakedefine SDL_FILE_DISABLED @SDL_FILE_DISABLED@
 #cmakedefine SDL_JOYSTICK_DISABLED @SDL_JOYSTICK_DISABLED@
 #cmakedefine SDL_HAPTIC_DISABLED @SDL_HAPTIC_DISABLED@
+#cmakedefine SDL_SENSOR_DISABLED @SDL_SENSOR_DISABLED@
 #cmakedefine SDL_LOADSO_DISABLED @SDL_LOADSO_DISABLED@
 #cmakedefine SDL_RENDER_DISABLED @SDL_RENDER_DISABLED@
 #cmakedefine SDL_THREADS_DISABLED @SDL_THREADS_DISABLED@
@@ -248,7 +272,6 @@
 #cmakedefine SDL_AUDIO_DRIVER_SUNAUDIO @SDL_AUDIO_DRIVER_SUNAUDIO@
 #cmakedefine SDL_AUDIO_DRIVER_WASAPI @SDL_AUDIO_DRIVER_WASAPI@
 #cmakedefine SDL_AUDIO_DRIVER_WINMM @SDL_AUDIO_DRIVER_WINMM@
-#cmakedefine SDL_AUDIO_DRIVER_XAUDIO2 @SDL_AUDIO_DRIVER_XAUDIO2@
 
 /* Enable various input drivers */
 #cmakedefine SDL_INPUT_LINUXEV @SDL_INPUT_LINUXEV@
@@ -265,6 +288,8 @@
 #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@
+// Urho3D - sync with SDL_config.h.in
+#cmakedefine SDL_JOYSTICK_HIDAPI @SDL_JOYSTICK_HIDAPI@
 #cmakedefine SDL_JOYSTICK_EMSCRIPTEN @SDL_JOYSTICK_EMSCRIPTEN@
 #cmakedefine SDL_HAPTIC_DUMMY @SDL_HAPTIC_DUMMY@
 #cmakedefine SDL_HAPTIC_LINUX @SDL_HAPTIC_LINUX@
@@ -273,6 +298,12 @@
 #cmakedefine SDL_HAPTIC_XINPUT @SDL_HAPTIC_XINPUT@
 #cmakedefine SDL_HAPTIC_ANDROID @SDL_HAPTIC_ANDROID@
 
+/* Enable various sensor drivers */
+#cmakedefine SDL_SENSOR_ANDROID @SDL_SENSOR_ANDROID@
+#cmakedefine SDL_SENSOR_DUMMY @SDL_SENSOR_DUMMY@
+// Urho3D - sync with SDL_config_iphoneos.h
+#cmakedefine SDL_SENSOR_COREMOTION @SDL_SENSOR_COREMOTION@
+
 /* Enable various shared object loading systems */
 #cmakedefine SDL_LOADSO_DLOPEN @SDL_LOADSO_DLOPEN@
 #cmakedefine SDL_LOADSO_DUMMY @SDL_LOADSO_DUMMY@
@@ -317,9 +348,6 @@
 #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_CURSOR@
 #cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_XKBCOMMON@
 
-#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@
@@ -349,6 +377,7 @@
 #cmakedefine SDL_VIDEO_RENDER_OGL_ES @SDL_VIDEO_RENDER_OGL_ES@
 #cmakedefine SDL_VIDEO_RENDER_OGL_ES2 @SDL_VIDEO_RENDER_OGL_ES2@
 #cmakedefine SDL_VIDEO_RENDER_DIRECTFB @SDL_VIDEO_RENDER_DIRECTFB@
+#cmakedefine SDL_VIDEO_RENDER_METAL @SDL_VIDEO_RENDER_METAL@
 
 /* Enable OpenGL support */
 #cmakedefine SDL_VIDEO_OPENGL @SDL_VIDEO_OPENGL@

+ 0 - 157
Source/ThirdParty/SDL/include/SDL_config_android.h

@@ -1,157 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_android_h_
-#define SDL_config_android_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-/**
- *  \file SDL_config_android.h
- *
- *  This is a configuration that can be used to build SDL for Android
- */
-
-#include <stdarg.h>
-
-#define HAVE_GCC_ATOMICS    1
-
-#define STDC_HEADERS    1
-#define HAVE_ALLOCA_H       1
-#define HAVE_CTYPE_H    1
-#define HAVE_INTTYPES_H 1
-#define HAVE_LIMITS_H   1
-#define HAVE_MATH_H 1
-#define HAVE_SIGNAL_H 1
-#define HAVE_STDINT_H   1
-#define HAVE_STDIO_H    1
-#define HAVE_STRING_H   1
-#define HAVE_SYS_TYPES_H    1
-
-/* C library functions */
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC    1
-#define HAVE_FREE   1
-#define HAVE_ALLOCA 1
-#define HAVE_GETENV 1
-#define HAVE_SETENV 1
-#define HAVE_PUTENV 1
-#define HAVE_SETENV 1
-#define HAVE_UNSETENV   1
-#define HAVE_QSORT  1
-#define HAVE_ABS    1
-#define HAVE_BCOPY  1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE    1
-#define HAVE_MEMCMP 1
-#define HAVE_STRLEN 1
-#define HAVE_STRLCPY    1
-#define HAVE_STRLCAT    1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR    1
-#define HAVE_STRSTR 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL    1
-#define HAVE_STRTOLL    1
-#define HAVE_STRTOULL   1
-#define HAVE_STRTOD 1
-#define HAVE_ATOI   1
-#define HAVE_ATOF 1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP    1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRNCASECMP 1
-#define HAVE_VSSCANF 1
-#define HAVE_VSNPRINTF  1
-#define HAVE_M_PI   1
-#define HAVE_ATAN   1
-#define HAVE_ATAN2  1
-#define HAVE_ACOS  1
-#define HAVE_ASIN  1
-#define HAVE_CEIL   1
-#define HAVE_COPYSIGN   1
-#define HAVE_COS    1
-#define HAVE_COSF   1
-#define HAVE_FABS   1
-#define HAVE_FLOOR  1
-#define HAVE_LOG    1
-#define HAVE_POW    1
-#define HAVE_SCALBN 1
-#define HAVE_SIN    1
-#define HAVE_SINF   1
-#define HAVE_SQRT   1
-#define HAVE_SQRTF  1
-#define HAVE_TAN    1
-#define HAVE_TANF   1
-#define HAVE_SIGACTION 1
-#define HAVE_SETJMP 1
-#define HAVE_NANOSLEEP  1
-#define HAVE_SYSCONF    1
-#define HAVE_CLOCK_GETTIME	1
-
-#define SIZEOF_VOIDP 4
-
-/* Enable various audio drivers */
-#define SDL_AUDIO_DRIVER_ANDROID    1
-#define SDL_AUDIO_DRIVER_DUMMY  1
-
-/* Enable various input drivers */
-#define SDL_JOYSTICK_ANDROID    1
-#define SDL_HAPTIC_ANDROID    1
-
-/* Enable various shared object loading systems */
-#define SDL_LOADSO_DLOPEN   1
-
-/* Enable various threading systems */
-#define SDL_THREAD_PTHREAD  1
-#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX  1
-
-/* Enable various timer systems */
-#define SDL_TIMER_UNIX  1
-
-/* Enable various video drivers */
-#define SDL_VIDEO_DRIVER_ANDROID 1
-
-/* Enable OpenGL ES */
-#define SDL_VIDEO_OPENGL_ES 1
-#define SDL_VIDEO_OPENGL_ES2 1
-#define SDL_VIDEO_OPENGL_EGL 1
-#define SDL_VIDEO_RENDER_OGL_ES 1
-#define SDL_VIDEO_RENDER_OGL_ES2    1
-
-/* Enable Vulkan support */
-/* Android does not support Vulkan in native code using the "armeabi" ABI. */
-#if defined(__ARM_ARCH) && __ARM_ARCH < 7
-#define SDL_VIDEO_VULKAN 0
-#else
-#define SDL_VIDEO_VULKAN 1
-#endif
-
-/* Enable system power support */
-#define SDL_POWER_ANDROID 1
-
-/* Enable the filesystem driver */
-#define SDL_FILESYSTEM_ANDROID   1
-
-#endif /* SDL_config_android_h_ */

+ 0 - 167
Source/ThirdParty/SDL/include/SDL_config_iphoneos.h

@@ -1,167 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_iphoneos_h_
-#define SDL_config_iphoneos_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-#ifdef __LP64__
-#define SIZEOF_VOIDP 8
-#else
-#define SIZEOF_VOIDP 4
-#endif
-
-#define HAVE_GCC_ATOMICS    1
-
-#define STDC_HEADERS    1
-#define HAVE_ALLOCA_H       1
-#define HAVE_CTYPE_H    1
-#define HAVE_INTTYPES_H 1
-#define HAVE_LIMITS_H   1
-#define HAVE_MATH_H 1
-#define HAVE_SIGNAL_H   1
-#define HAVE_STDINT_H   1
-#define HAVE_STDIO_H    1
-#define HAVE_STRING_H   1
-#define HAVE_SYS_TYPES_H    1
-#define HAVE_LIBUNWIND_H    1
-
-/* C library functions */
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC    1
-#define HAVE_FREE   1
-#define HAVE_ALLOCA 1
-#define HAVE_GETENV 1
-#define HAVE_SETENV 1
-#define HAVE_PUTENV 1
-#define HAVE_SETENV 1
-#define HAVE_UNSETENV   1
-#define HAVE_QSORT  1
-#define HAVE_ABS    1
-#define HAVE_BCOPY  1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE    1
-#define HAVE_MEMCMP 1
-#define HAVE_STRLEN 1
-#define HAVE_STRLCPY    1
-#define HAVE_STRLCAT    1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR    1
-#define HAVE_STRSTR 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL    1
-#define HAVE_STRTOLL    1
-#define HAVE_STRTOULL   1
-#define HAVE_STRTOD 1
-#define HAVE_ATOI   1
-#define HAVE_ATOF   1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP    1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRNCASECMP 1
-#define HAVE_VSSCANF 1
-#define HAVE_VSNPRINTF  1
-#define HAVE_M_PI   1
-#define HAVE_ATAN   1
-#define HAVE_ATAN2  1
-#define HAVE_ACOS  1
-#define HAVE_ASIN  1
-#define HAVE_CEIL   1
-#define HAVE_COPYSIGN   1
-#define HAVE_COS    1
-#define HAVE_COSF   1
-#define HAVE_FABS   1
-#define HAVE_FLOOR  1
-#define HAVE_LOG    1
-#define HAVE_POW    1
-#define HAVE_SCALBN 1
-#define HAVE_SIN    1
-#define HAVE_SINF   1
-#define HAVE_SQRT   1
-#define HAVE_SQRTF  1
-#define HAVE_TAN    1
-#define HAVE_TANF   1
-#define HAVE_SIGACTION  1
-#define HAVE_SETJMP 1
-#define HAVE_NANOSLEEP  1
-#define HAVE_SYSCONF    1
-#define HAVE_SYSCTLBYNAME 1
-
-/* enable iPhone version of Core Audio driver */
-#define SDL_AUDIO_DRIVER_COREAUDIO 1
-/* Enable the dummy audio driver (src/audio/dummy/\*.c) */
-#define SDL_AUDIO_DRIVER_DUMMY  1
-
-/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */
-#define SDL_HAPTIC_DUMMY 1
-
-/* Enable MFi joystick support */
-#define SDL_JOYSTICK_MFI 1
-
-/* Enable Unix style SO loading */
-#define SDL_LOADSO_DLOPEN 1
-
-/* Enable various threading systems */
-#define SDL_THREAD_PTHREAD  1
-#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX  1
-
-/* Enable various timer systems */
-#define SDL_TIMER_UNIX  1
-
-/* Supported video drivers */
-#define SDL_VIDEO_DRIVER_UIKIT  1
-#define SDL_VIDEO_DRIVER_DUMMY  1
-
-/* enable OpenGL ES */
-#define SDL_VIDEO_OPENGL_ES2 1
-#define SDL_VIDEO_OPENGL_ES 1
-#define SDL_VIDEO_RENDER_OGL_ES 1
-#define SDL_VIDEO_RENDER_OGL_ES2    1
-
-/* Enable Vulkan support on 64-bit devices when an iOS 8+ SDK is used. */
-#if !TARGET_OS_SIMULATOR && !TARGET_CPU_ARM && defined(__IPHONE_8_0)
-#define SDL_VIDEO_VULKAN 1
-#else
-#define SDL_VIDEO_VULKAN 0
-#endif
-
-/* Enable system power support */
-#define SDL_POWER_UIKIT 1
-
-/* enable iPhone keyboard support */
-#define SDL_IPHONE_KEYBOARD 1
-
-/* enable iOS extended launch screen */
-#define SDL_IPHONE_LAUNCHSCREEN 1
-
-/* Set max recognized G-force from accelerometer
-   See src/joystick/uikit/SDL_sysjoystick.m for notes on why this is needed
- */
-#define SDL_IPHONE_MAX_GFORCE 5.0
-
-/* enable filesystem support */
-#define SDL_FILESYSTEM_COCOA   1
-
-#endif /* SDL_config_iphoneos_h_ */

+ 0 - 199
Source/ThirdParty/SDL/include/SDL_config_macosx.h

@@ -1,199 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_macosx_h_
-#define SDL_config_macosx_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-/* This gets us MAC_OS_X_VERSION_MIN_REQUIRED... */
-#include <AvailabilityMacros.h>
-
-/* This is a set of defines to configure the SDL features */
-
-#ifdef __LP64__
-    #define SIZEOF_VOIDP 8
-#else
-    #define SIZEOF_VOIDP 4
-#endif
-
-/* Useful headers */
-#define STDC_HEADERS    1
-#define HAVE_ALLOCA_H       1
-#define HAVE_CTYPE_H    1
-#define HAVE_FLOAT_H    1
-#define HAVE_INTTYPES_H 1
-#define HAVE_LIMITS_H   1
-#define HAVE_MATH_H 1
-#define HAVE_SIGNAL_H   1
-#define HAVE_STDINT_H   1
-#define HAVE_STDIO_H    1
-#define HAVE_STRING_H   1
-#define HAVE_SYS_TYPES_H    1
-#define HAVE_LIBUNWIND_H    1
-
-/* C library functions */
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC    1
-#define HAVE_FREE   1
-#define HAVE_ALLOCA 1
-#define HAVE_GETENV 1
-#define HAVE_SETENV 1
-#define HAVE_PUTENV 1
-#define HAVE_UNSETENV   1
-#define HAVE_QSORT  1
-#define HAVE_ABS    1
-#define HAVE_BCOPY  1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE    1
-#define HAVE_MEMCMP 1
-#define HAVE_STRLEN 1
-#define HAVE_STRLCPY    1
-#define HAVE_STRLCAT    1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR    1
-#define HAVE_STRSTR 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL    1
-#define HAVE_STRTOLL    1
-#define HAVE_STRTOULL   1
-#define HAVE_STRTOD 1
-#define HAVE_ATOI   1
-#define HAVE_ATOF   1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP    1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRNCASECMP 1
-#define HAVE_VSSCANF 1
-#define HAVE_VSNPRINTF  1
-#define HAVE_CEIL   1
-#define HAVE_COPYSIGN   1
-#define HAVE_COS    1
-#define HAVE_COSF   1
-#define HAVE_FABS   1
-#define HAVE_FLOOR  1
-#define HAVE_LOG    1
-#define HAVE_POW    1
-#define HAVE_SCALBN 1
-#define HAVE_SIN    1
-#define HAVE_SINF   1
-#define HAVE_SQRT   1
-#define HAVE_SQRTF  1
-#define HAVE_TAN    1
-#define HAVE_TANF   1
-#define HAVE_SIGACTION  1
-#define HAVE_SETJMP 1
-#define HAVE_NANOSLEEP  1
-#define HAVE_SYSCONF    1
-#define HAVE_SYSCTLBYNAME 1
-#define HAVE_ATAN 1
-#define HAVE_ATAN2 1
-#define HAVE_ACOS 1
-#define HAVE_ASIN 1
-
-/* Enable various audio drivers */
-#define SDL_AUDIO_DRIVER_COREAUDIO  1
-#define SDL_AUDIO_DRIVER_DISK   1
-#define SDL_AUDIO_DRIVER_DUMMY  1
-
-/* Enable various input drivers */
-#define SDL_JOYSTICK_IOKIT  1
-#define SDL_HAPTIC_IOKIT    1
-
-/* Enable various shared object loading systems */
-#define SDL_LOADSO_DLOPEN   1
-
-/* Enable various threading systems */
-#define SDL_THREAD_PTHREAD  1
-#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX  1
-
-/* Enable various timer systems */
-#define SDL_TIMER_UNIX  1
-
-/* Enable various video drivers */
-#define SDL_VIDEO_DRIVER_COCOA  1
-#define SDL_VIDEO_DRIVER_DUMMY  1
-#undef SDL_VIDEO_DRIVER_X11
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC "/usr/X11R6/lib/libX11.6.dylib"
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT "/usr/X11R6/lib/libXext.6.dylib"
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA "/usr/X11R6/lib/libXinerama.1.dylib"
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 "/usr/X11R6/lib/libXi.6.dylib"
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR "/usr/X11R6/lib/libXrandr.2.dylib"
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS "/usr/X11R6/lib/libXss.1.dylib"
-#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE "/usr/X11R6/lib/libXxf86vm.1.dylib"
-#define SDL_VIDEO_DRIVER_X11_XDBE 1
-#define SDL_VIDEO_DRIVER_X11_XINERAMA 1
-#define SDL_VIDEO_DRIVER_X11_XRANDR 1
-#define SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1
-#define SDL_VIDEO_DRIVER_X11_XSHAPE 1
-#define SDL_VIDEO_DRIVER_X11_XVIDMODE 1
-#define SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM 1
-
-#ifdef MAC_OS_X_VERSION_10_8
-/*
- * No matter the versions targeted, this is the 10.8 or later SDK, so you have
- *  to use the external Xquartz, which is a more modern Xlib. Previous SDKs
- *  used an older Xlib.
- */
-#define SDL_VIDEO_DRIVER_X11_XINPUT2 1
-#define SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1
-#define SDL_VIDEO_DRIVER_X11_CONST_PARAM_XEXTADDDISPLAY 1
-#endif
-
-#ifndef SDL_VIDEO_RENDER_OGL
-#define SDL_VIDEO_RENDER_OGL    1
-#endif
-
-/* Enable OpenGL support */
-#ifndef SDL_VIDEO_OPENGL
-#define SDL_VIDEO_OPENGL    1
-#endif
-#ifndef SDL_VIDEO_OPENGL_CGL
-#define SDL_VIDEO_OPENGL_CGL    1
-#endif
-#ifndef SDL_VIDEO_OPENGL_GLX
-#define SDL_VIDEO_OPENGL_GLX    1
-#endif
-
-/* Enable Vulkan support */
-/* Metal/MoltenVK/Vulkan only supported on 64-bit architectures with 10.11+ */
-#if TARGET_CPU_X86_64 && (MAC_OS_X_VERSION_MAX_ALLOWED >= 101100)
-#define SDL_VIDEO_VULKAN 1
-#else
-#define  SDL_VIDEO_VULKAN 0
-#endif
-
-/* Enable system power support */
-#define SDL_POWER_MACOSX 1
-
-/* enable filesystem support */
-#define SDL_FILESYSTEM_COCOA   1
-
-/* Enable assembly routines */
-#define SDL_ASSEMBLY_ROUTINES   1
-#ifdef __ppc__
-#define SDL_ALTIVEC_BLITTERS    1
-#endif
-
-#endif /* SDL_config_macosx_h_ */

+ 0 - 82
Source/ThirdParty/SDL/include/SDL_config_minimal.h

@@ -1,82 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_minimal_h_
-#define SDL_config_minimal_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-/**
- *  \file SDL_config_minimal.h
- *
- *  This is the minimal configuration that can be used to build SDL.
- */
-
-#define HAVE_STDARG_H   1
-#define HAVE_STDDEF_H   1
-
-/* Most everything except Visual Studio 2008 and earlier has stdint.h now */
-#if defined(_MSC_VER) && (_MSC_VER < 1600)
-/* Here are some reasonable defaults */
-typedef unsigned int size_t;
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef signed short int16_t;
-typedef unsigned short uint16_t;
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-typedef signed long long int64_t;
-typedef unsigned long long uint64_t;
-typedef unsigned long uintptr_t;
-#else
-#define HAVE_STDINT_H 1
-#endif /* Visual Studio 2008 */
-
-#ifdef __GNUC__
-#define HAVE_GCC_SYNC_LOCK_TEST_AND_SET 1
-#endif
-
-/* Enable the dummy audio driver (src/audio/dummy/\*.c) */
-#define SDL_AUDIO_DRIVER_DUMMY  1
-
-/* Enable the stub joystick driver (src/joystick/dummy/\*.c) */
-#define SDL_JOYSTICK_DISABLED   1
-
-/* Enable the stub haptic driver (src/haptic/dummy/\*.c) */
-#define SDL_HAPTIC_DISABLED 1
-
-/* Enable the stub shared object loader (src/loadso/dummy/\*.c) */
-#define SDL_LOADSO_DISABLED 1
-
-/* Enable the stub thread support (src/thread/generic/\*.c) */
-#define SDL_THREADS_DISABLED    1
-
-/* Enable the stub timer support (src/timer/dummy/\*.c) */
-#define SDL_TIMERS_DISABLED 1
-
-/* Enable the dummy video driver (src/video/dummy/\*.c) */
-#define SDL_VIDEO_DRIVER_DUMMY  1
-
-/* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */
-#define SDL_FILESYSTEM_DUMMY  1
-
-#endif /* SDL_config_minimal_h_ */

+ 0 - 129
Source/ThirdParty/SDL/include/SDL_config_pandora.h

@@ -1,129 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_pandora_h_
-#define SDL_config_pandora_h_
-#define SDL_config_h_
-
-/* This is a set of defines to configure the SDL features */
-
-/* General platform specific identifiers */
-#include "SDL_platform.h"
-
-#ifdef __LP64__
-#define SIZEOF_VOIDP 8
-#else
-#define SIZEOF_VOIDP 4
-#endif
-
-#define SDL_BYTEORDER 1234
-
-#define STDC_HEADERS 1
-#define HAVE_ALLOCA_H 1
-#define HAVE_CTYPE_H 1
-#define HAVE_ICONV_H 1
-#define HAVE_INTTYPES_H 1
-#define HAVE_LIMITS_H 1
-#define HAVE_MALLOC_H 1
-#define HAVE_MATH_H 1
-#define HAVE_MEMORY_H 1
-#define HAVE_SIGNAL_H 1
-#define HAVE_STDARG_H 1
-#define HAVE_STDINT_H 1
-#define HAVE_STDIO_H 1
-#define HAVE_STDLIB_H 1
-#define HAVE_STRINGS_H 1
-#define HAVE_STRING_H 1
-#define HAVE_SYS_TYPES_H 1
-
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC 1
-#define HAVE_FREE 1
-#define HAVE_ALLOCA 1
-#define HAVE_GETENV 1
-#define HAVE_SETENV 1
-#define HAVE_PUTENV 1
-#define HAVE_UNSETENV 1
-#define HAVE_QSORT 1
-#define HAVE_ABS 1
-#define HAVE_BCOPY 1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE 1
-#define HAVE_STRLEN 1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR 1
-#define HAVE_STRSTR 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL 1
-#define HAVE_STRTOLL 1
-#define HAVE_STRTOULL 1
-#define HAVE_ATOI 1
-#define HAVE_ATOF 1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP 1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRNCASECMP 1
-#define HAVE_VSSCANF 1
-#define HAVE_VSNPRINTF 1
-#define HAVE_M_PI 1
-#define HAVE_CEIL 1
-#define HAVE_COPYSIGN 1
-#define HAVE_COS 1
-#define HAVE_COSF 1
-#define HAVE_FABS 1
-#define HAVE_FLOOR 1
-#define HAVE_LOG 1
-#define HAVE_SCALBN 1
-#define HAVE_SIN 1
-#define HAVE_SINF 1
-#define HAVE_SQRT 1
-#define HAVE_SQRTF 1
-#define HAVE_TAN 1
-#define HAVE_TANF 1
-#define HAVE_SIGACTION 1
-#define HAVE_SETJMP 1
-#define HAVE_NANOSLEEP 1
-
-#define SDL_AUDIO_DRIVER_DUMMY 1
-#define SDL_AUDIO_DRIVER_OSS 1
-
-#define SDL_INPUT_LINUXEV 1
-#define SDL_INPUT_TSLIB 1
-#define SDL_JOYSTICK_LINUX 1
-#define SDL_HAPTIC_LINUX 1
-
-#define SDL_LOADSO_DLOPEN 1
-
-#define SDL_THREAD_PTHREAD 1
-#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP 1
-
-#define SDL_TIMER_UNIX 1
-#define SDL_FILESYSTEM_UNIX 1
-
-#define SDL_VIDEO_DRIVER_DUMMY 1
-#define SDL_VIDEO_DRIVER_X11 1
-#define SDL_VIDEO_DRIVER_PANDORA 1
-#define SDL_VIDEO_RENDER_OGL_ES 1
-#define SDL_VIDEO_OPENGL_ES 1
-
-#endif /* SDL_config_pandora_h_ */

+ 0 - 144
Source/ThirdParty/SDL/include/SDL_config_psp.h

@@ -1,144 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_psp_h_
-#define SDL_config_psp_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-
-
-#ifdef __GNUC__
-#define HAVE_GCC_SYNC_LOCK_TEST_AND_SET 1
-#endif
-
-#define HAVE_GCC_ATOMICS    1
-
-#define STDC_HEADERS    1
-#define HAVE_ALLOCA_H       1
-#define HAVE_CTYPE_H    1
-#define HAVE_INTTYPES_H 1
-#define HAVE_LIMITS_H   1
-#define HAVE_MATH_H 1
-#define HAVE_SIGNAL_H   1
-#define HAVE_STDINT_H   1
-#define HAVE_STDIO_H    1
-#define HAVE_STRING_H   1
-#define HAVE_SYS_TYPES_H    1
-
-/* C library functions */
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC    1
-#define HAVE_FREE   1
-#define HAVE_ALLOCA 1
-#define HAVE_GETENV 1
-#define HAVE_SETENV 1
-#define HAVE_PUTENV 1
-#define HAVE_SETENV 1
-#define HAVE_UNSETENV   1
-#define HAVE_QSORT  1
-#define HAVE_ABS    1
-#define HAVE_BCOPY  1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE    1
-#define HAVE_MEMCMP 1
-#define HAVE_STRLEN 1
-#define HAVE_STRLCPY    1
-#define HAVE_STRLCAT    1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR    1
-#define HAVE_STRSTR 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL    1
-#define HAVE_STRTOLL    1
-#define HAVE_STRTOULL   1
-#define HAVE_STRTOD 1
-#define HAVE_ATOI   1
-#define HAVE_ATOF   1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP    1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRNCASECMP 1
-#define HAVE_VSSCANF 1
-#define HAVE_VSNPRINTF  1
-#define HAVE_M_PI   1
-#define HAVE_ATAN   1
-#define HAVE_ATAN2  1
-#define HAVE_ACOS  1
-#define HAVE_ASIN  1
-#define HAVE_CEIL   1
-#define HAVE_COPYSIGN   1
-#define HAVE_COS    1
-#define HAVE_COSF   1
-#define HAVE_FABS   1
-#define HAVE_FLOOR  1
-#define HAVE_LOG    1
-#define HAVE_POW    1
-#define HAVE_SCALBN 1
-#define HAVE_SIN    1
-#define HAVE_SINF   1
-#define HAVE_SQRT   1
-#define HAVE_SQRTF  1
-#define HAVE_TAN    1
-#define HAVE_TANF   1
-#define HAVE_SETJMP 1
-#define HAVE_NANOSLEEP  1
-/* #define HAVE_SYSCONF  1 */
-/* #define HAVE_SIGACTION    1 */
-
-
-/* PSP isn't that sophisticated */
-#define LACKS_SYS_MMAN_H 1
-
-/* Enable the stub thread support (src/thread/psp/\*.c) */
-#define SDL_THREAD_PSP  1
-
-/* Enable the stub timer support (src/timer/psp/\*.c) */
-#define SDL_TIMERS_PSP  1
-
-/* Enable the stub joystick driver (src/joystick/psp/\*.c) */
-#define SDL_JOYSTICK_PSP        1
-
-/* Enable the stub audio driver (src/audio/psp/\*.c) */
-#define SDL_AUDIO_DRIVER_PSP    1
-
-/* PSP video dirver */
-#define SDL_VIDEO_DRIVER_PSP   1
-
-/* PSP render dirver */
-#define SDL_VIDEO_RENDER_PSP   1
-
-#define SDL_POWER_PSP          1
-
-/* !!! FIXME: what does PSP do for filesystem stuff? */
-#define SDL_FILESYSTEM_DUMMY   1
-
-/* PSP doesn't have haptic device (src/haptic/dummy/\*.c) */
-#define SDL_HAPTIC_DISABLED    1
-
-/* PSP can't load shared object (src/loadso/dummy/\*.c) */
-#define SDL_LOADSO_DISABLED    1
-
-
-#endif /* SDL_config_psp_h_ */

+ 0 - 239
Source/ThirdParty/SDL/include/SDL_config_windows.h

@@ -1,239 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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.
-*/
-
-// Modified by Lasse Oorni for Urho3D
-
-#ifndef SDL_config_windows_h_
-#define SDL_config_windows_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-/* This is a set of defines to configure the SDL features */
-
-#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
-#if defined(__GNUC__) || defined(__DMC__) || defined(__WATCOMC__)
-#define HAVE_STDINT_H   1
-#elif defined(_MSC_VER)
-typedef signed __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef signed __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef signed __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef signed __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-#ifndef _UINTPTR_T_DEFINED
-#ifdef  _WIN64
-typedef unsigned __int64 uintptr_t;
-#else
-typedef unsigned int uintptr_t;
-#endif
-#define _UINTPTR_T_DEFINED
-#endif
-/* Older Visual C++ headers don't have the Win64-compatible typedefs... */
-#if ((_MSC_VER <= 1200) && (!defined(DWORD_PTR)))
-#define DWORD_PTR DWORD
-#endif
-#if ((_MSC_VER <= 1200) && (!defined(LONG_PTR)))
-#define LONG_PTR LONG
-#endif
-#else /* !__GNUC__ && !_MSC_VER */
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef signed short int16_t;
-typedef unsigned short uint16_t;
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-typedef signed long long int64_t;
-typedef unsigned long long uint64_t;
-#ifndef _SIZE_T_DEFINED_
-#define _SIZE_T_DEFINED_
-typedef unsigned int size_t;
-#endif
-typedef unsigned int uintptr_t;
-#endif /* __GNUC__ || _MSC_VER */
-#endif /* !_STDINT_H_ && !HAVE_STDINT_H */
-
-#ifdef _WIN64
-# define SIZEOF_VOIDP 8
-#else
-# define SIZEOF_VOIDP 4
-#endif
-
-#define HAVE_DDRAW_H 1
-#define HAVE_DINPUT_H 1
-#define HAVE_DSOUND_H 1
-#define HAVE_DXGI_H 1
-#define HAVE_XINPUT_H 1
-
-/* This is disabled by default to avoid C runtime dependencies and manifest requirements */
-#ifdef HAVE_LIBC
-/* Useful headers */
-#define STDC_HEADERS 1
-#define HAVE_CTYPE_H 1
-#define HAVE_FLOAT_H 1
-#define HAVE_LIMITS_H 1
-#define HAVE_MATH_H 1
-#define HAVE_SIGNAL_H 1
-#define HAVE_STDIO_H 1
-#define HAVE_STRING_H 1
-
-/* C library functions */
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC 1
-#define HAVE_FREE 1
-#define HAVE_ALLOCA 1
-#define HAVE_QSORT 1
-#define HAVE_ABS 1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE 1
-#define HAVE_MEMCMP 1
-#define HAVE_STRLEN 1
-#define HAVE__STRREV 1
-#define HAVE__STRUPR 1
-#define HAVE__STRLWR 1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR 1
-#define HAVE_STRSTR 1
-#define HAVE__LTOA 1
-#define HAVE__ULTOA 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL 1
-#define HAVE_STRTOD 1
-#define HAVE_ATOI 1
-#define HAVE_ATOF 1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP 1
-#define HAVE__STRICMP 1
-#define HAVE__STRNICMP 1
-#define HAVE_ATAN 1
-#define HAVE_ATAN2 1
-#define HAVE_ACOS  1
-#define HAVE_ASIN  1
-#define HAVE_CEIL 1
-#define HAVE_COS 1
-#define HAVE_COSF 1
-#define HAVE_FABS 1
-#define HAVE_FLOOR 1
-#define HAVE_LOG 1
-#define HAVE_POW 1
-#define HAVE_SIN 1
-#define HAVE_SINF 1
-#define HAVE_SQRT 1
-#define HAVE_SQRTF 1
-#define HAVE_TAN 1
-#define HAVE_TANF 1
-#define HAVE__COPYSIGN 1
-#if defined(_MSC_VER)
-/* These functions were added with the VC++ 2013 C runtime library */
-#if _MSC_VER >= 1800
-#define HAVE_STRTOLL 1
-#define HAVE_VSSCANF 1
-#define HAVE_SCALBN 1
-#endif
-/* This function is available with at least the VC++ 2008 C runtime library */
-#if _MSC_VER >= 1400
-#define HAVE__FSEEKI64 1
-#endif
-#endif
-#if !defined(_MSC_VER) || defined(_USE_MATH_DEFINES)
-#define HAVE_M_PI 1
-#endif
-#else
-#define HAVE_STDARG_H   1
-#define HAVE_STDDEF_H   1
-#endif
-
-/* Enable various audio drivers */
-#define SDL_AUDIO_DRIVER_WASAPI 1
-#define SDL_AUDIO_DRIVER_DSOUND 1
-#define SDL_AUDIO_DRIVER_XAUDIO2    0
-// Urho3D: do not use winmm
-#define SDL_AUDIO_DRIVER_WINMM  0
-#define SDL_AUDIO_DRIVER_DISK   1
-#define SDL_AUDIO_DRIVER_DUMMY  1
-
-/* Enable various input drivers */
-// Urho3D: do not enable joystick & force feedback support by default, requires certain include files
-//#define SDL_JOYSTICK_DINPUT 1
-//#define SDL_JOYSTICK_XINPUT 1
-//#define SDL_HAPTIC_DINPUT   1
-//#define SDL_HAPTIC_XINPUT   1
-
-/* Enable various shared object loading systems */
-#define SDL_LOADSO_WINDOWS  1
-
-/* Enable various threading systems */
-#define SDL_THREAD_WINDOWS  1
-
-/* Enable various timer systems */
-#define SDL_TIMER_WINDOWS   1
-
-/* Enable various video drivers */
-#define SDL_VIDEO_DRIVER_DUMMY  1
-#define SDL_VIDEO_DRIVER_WINDOWS    1
-
-#ifndef SDL_VIDEO_RENDER_D3D
-#define SDL_VIDEO_RENDER_D3D    1
-#endif
-#ifndef SDL_VIDEO_RENDER_D3D11
-#define SDL_VIDEO_RENDER_D3D11	0
-#endif
-
-/* Enable OpenGL support */
-#ifndef SDL_VIDEO_OPENGL
-#define SDL_VIDEO_OPENGL    1
-#endif
-#ifndef SDL_VIDEO_OPENGL_WGL
-#define SDL_VIDEO_OPENGL_WGL    1
-#endif
-#ifndef SDL_VIDEO_RENDER_OGL
-#define SDL_VIDEO_RENDER_OGL    1
-#endif
-// Urho3D: Disable OpenGL ES on Windows
-#ifndef SDL_VIDEO_RENDER_OGL_ES2
-#define SDL_VIDEO_RENDER_OGL_ES2    0
-#endif
-#ifndef SDL_VIDEO_OPENGL_ES2
-#define SDL_VIDEO_OPENGL_ES2    0
-#endif
-#ifndef SDL_VIDEO_OPENGL_EGL
-#define SDL_VIDEO_OPENGL_EGL    0
-#endif
-
-/* Enable Vulkan support */
-#define SDL_VIDEO_VULKAN 1
-
-/* Enable system power support */
-#define SDL_POWER_WINDOWS 1
-
-/* Enable filesystem support */
-#define SDL_FILESYSTEM_WINDOWS  1
-
-/* Enable assembly routines (Win64 doesn't have inline asm) */
-#ifndef _WIN64
-#define SDL_ASSEMBLY_ROUTINES   1
-#endif
-
-#endif /* SDL_config_windows_h_ */

+ 0 - 216
Source/ThirdParty/SDL/include/SDL_config_winrt.h

@@ -1,216 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_winrt_h_
-#define SDL_config_winrt_h_
-#define SDL_config_h_
-
-#include "SDL_platform.h"
-
-/* Make sure the Windows SDK's NTDDI_VERSION macro gets defined.  This is used
-   by SDL to determine which version of the Windows SDK is being used.
-*/
-#include <sdkddkver.h>
-
-/* Define possibly-undefined NTDDI values (used when compiling SDL against
-   older versions of the Windows SDK.
-*/
-#ifndef NTDDI_WINBLUE
-#define NTDDI_WINBLUE 0x06030000
-#endif
-#ifndef NTDDI_WIN10
-#define NTDDI_WIN10 0x0A000000
-#endif
-
-/* This is a set of defines to configure the SDL features */
-
-#if !defined(_STDINT_H_) && (!defined(HAVE_STDINT_H) || !_HAVE_STDINT_H)
-#if defined(__GNUC__) || defined(__DMC__) || defined(__WATCOMC__)
-#define HAVE_STDINT_H	1
-#elif defined(_MSC_VER)
-typedef signed __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef signed __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef signed __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef signed __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-#ifndef _UINTPTR_T_DEFINED
-#ifdef  _WIN64
-typedef unsigned __int64 uintptr_t;
-#else
-typedef unsigned int uintptr_t;
-#endif
-#define _UINTPTR_T_DEFINED
-#endif
-/* Older Visual C++ headers don't have the Win64-compatible typedefs... */
-#if ((_MSC_VER <= 1200) && (!defined(DWORD_PTR)))
-#define DWORD_PTR DWORD
-#endif
-#if ((_MSC_VER <= 1200) && (!defined(LONG_PTR)))
-#define LONG_PTR LONG
-#endif
-#else /* !__GNUC__ && !_MSC_VER */
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef signed short int16_t;
-typedef unsigned short uint16_t;
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-typedef signed long long int64_t;
-typedef unsigned long long uint64_t;
-#ifndef _SIZE_T_DEFINED_
-#define _SIZE_T_DEFINED_
-typedef unsigned int size_t;
-#endif
-typedef unsigned int uintptr_t;
-#endif /* __GNUC__ || _MSC_VER */
-#endif /* !_STDINT_H_ && !HAVE_STDINT_H */
-
-#ifdef _WIN64
-# define SIZEOF_VOIDP 8
-#else
-# define SIZEOF_VOIDP 4
-#endif
-
-/* Useful headers */
-#define HAVE_DXGI_H 1
-#if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
-#define HAVE_XINPUT_H 1
-#endif
-#define HAVE_LIBC 1
-#define STDC_HEADERS 1
-#define HAVE_CTYPE_H 1
-#define HAVE_FLOAT_H 1
-#define HAVE_LIMITS_H 1
-#define HAVE_MATH_H 1
-#define HAVE_SIGNAL_H 1
-#define HAVE_STDIO_H 1
-#define HAVE_STRING_H 1
-
-/* C library functions */
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC 1
-#define HAVE_FREE 1
-#define HAVE_ALLOCA 1
-#define HAVE_QSORT 1
-#define HAVE_ABS 1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE 1
-#define HAVE_MEMCMP 1
-#define HAVE_STRLEN 1
-#define HAVE__STRREV 1
-#define HAVE__STRUPR 1
-//#define HAVE__STRLWR 1	// TODO, WinRT: consider using _strlwr_s instead
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR 1
-#define HAVE_STRSTR 1
-//#define HAVE_ITOA 1   // TODO, WinRT: consider using _itoa_s instead
-//#define HAVE__LTOA 1	// TODO, WinRT: consider using _ltoa_s instead
-//#define HAVE__ULTOA 1	// TODO, WinRT: consider using _ultoa_s instead
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL 1
-//#define HAVE_STRTOLL 1
-#define HAVE_STRTOD 1
-#define HAVE_ATOI 1
-#define HAVE_ATOF 1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP 1
-#define HAVE__STRICMP 1
-#define HAVE__STRNICMP 1
-#define HAVE_VSNPRINTF 1
-//#define HAVE_SSCANF 1	// TODO, WinRT: consider using sscanf_s instead
-#define HAVE_M_PI 1
-#define HAVE_ATAN 1
-#define HAVE_ATAN2 1
-#define HAVE_CEIL 1
-#define HAVE__COPYSIGN 1
-#define HAVE_COS 1
-#define HAVE_COSF 1
-#define HAVE_FABS 1
-#define HAVE_FLOOR 1
-#define HAVE_LOG 1
-#define HAVE_POW 1
-//#define HAVE_SCALBN 1
-#define HAVE__SCALB 1
-#define HAVE_SIN 1
-#define HAVE_SINF 1
-#define HAVE_SQRT 1
-#define HAVE_SQRTF 1
-#define HAVE_TAN 1
-#define HAVE_TANF 1
-#define HAVE__FSEEKI64 1
-
-/* Enable various audio drivers */
-#define SDL_AUDIO_DRIVER_XAUDIO2	1
-#define SDL_AUDIO_DRIVER_DISK	1
-#define SDL_AUDIO_DRIVER_DUMMY	1
-
-/* Enable various input drivers */
-#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
-#define SDL_JOYSTICK_DISABLED 1
-#define SDL_HAPTIC_DISABLED	1
-#else
-#define SDL_JOYSTICK_XINPUT 1
-#define SDL_HAPTIC_XINPUT   1
-#endif
-
-/* Enable various shared object loading systems */
-#define SDL_LOADSO_WINDOWS	1
-
-/* Enable various threading systems */
-#if (NTDDI_VERSION >= NTDDI_WINBLUE)
-#define SDL_THREAD_WINDOWS  1
-#else
-/* WinRT on Windows 8.0 and Windows Phone 8.0 don't support CreateThread() */
-#define SDL_THREAD_STDCPP   1
-#endif
-
-/* Enable various timer systems */
-#define SDL_TIMER_WINDOWS	1
-
-/* Enable various video drivers */
-#define SDL_VIDEO_DRIVER_WINRT	1
-#define SDL_VIDEO_DRIVER_DUMMY  1
-
-/* Enable OpenGL ES 2.0 (via a modified ANGLE library) */
-#define SDL_VIDEO_OPENGL_ES2 1
-#define SDL_VIDEO_OPENGL_EGL 1
-
-/* Enable appropriate renderer(s) */
-#define SDL_VIDEO_RENDER_D3D11  1
-
-#if SDL_VIDEO_OPENGL_ES2
-#define SDL_VIDEO_RENDER_OGL_ES2 1
-#endif
-
-/* Enable system power support */
-#define SDL_POWER_WINRT 1
-
-/* Enable assembly routines (Win64 doesn't have inline asm) */
-#ifndef _WIN64
-#define SDL_ASSEMBLY_ROUTINES	1
-#endif
-
-#endif /* SDL_config_winrt_h_ */

+ 0 - 122
Source/ThirdParty/SDL/include/SDL_config_wiz.h

@@ -1,122 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
-
-  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_config_wiz_h_
-#define SDL_config_wiz_h_
-#define SDL_config_h_
-
-/* This is a set of defines to configure the SDL features */
-
-/* General platform specific identifiers */
-#include "SDL_platform.h"
-
-#define SDL_BYTEORDER 1234
-
-#define STDC_HEADERS 1
-#define HAVE_ALLOCA_H 1
-#define HAVE_CTYPE_H 1
-#define HAVE_ICONV_H 1
-#define HAVE_INTTYPES_H 1
-#define HAVE_LIMITS_H 1
-#define HAVE_MALLOC_H 1
-#define HAVE_MATH_H 1
-#define HAVE_MEMORY_H 1
-#define HAVE_SIGNAL_H 1
-#define HAVE_STDARG_H 1
-#define HAVE_STDINT_H 1
-#define HAVE_STDIO_H 1
-#define HAVE_STDLIB_H 1
-#define HAVE_STRINGS_H 1
-#define HAVE_STRING_H 1
-#define HAVE_SYS_TYPES_H 1
-
-#define HAVE_MALLOC 1
-#define HAVE_CALLOC 1
-#define HAVE_REALLOC 1
-#define HAVE_FREE 1
-#define HAVE_ALLOCA 1
-#define HAVE_GETENV 1
-#define HAVE_SETENV 1
-#define HAVE_PUTENV 1
-#define HAVE_UNSETENV 1
-#define HAVE_QSORT 1
-#define HAVE_ABS 1
-#define HAVE_BCOPY 1
-#define HAVE_MEMSET 1
-#define HAVE_MEMCPY 1
-#define HAVE_MEMMOVE 1
-#define HAVE_STRLEN 1
-#define HAVE_STRCHR 1
-#define HAVE_STRRCHR 1
-#define HAVE_STRSTR 1
-#define HAVE_STRTOL 1
-#define HAVE_STRTOUL 1
-#define HAVE_STRTOLL 1
-#define HAVE_STRTOULL 1
-#define HAVE_ATOI 1
-#define HAVE_ATOF 1
-#define HAVE_STRCMP 1
-#define HAVE_STRNCMP 1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRNCASECMP 1
-#define HAVE_VSSCANF 1
-#define HAVE_VSNPRINTF 1
-#define HAVE_M_PI 1
-#define HAVE_CEIL 1
-#define HAVE_COPYSIGN 1
-#define HAVE_COS 1
-#define HAVE_COSF 1
-#define HAVE_FABS 1
-#define HAVE_FLOOR 1
-#define HAVE_LOG 1
-#define HAVE_SCALBN 1
-#define HAVE_SIN 1
-#define HAVE_SINF 1
-#define HAVE_SQRT 1
-#define HAVE_SQRTF 1
-#define HAVE_TAN 1
-#define HAVE_TANF 1
-#define HAVE_SIGACTION 1
-#define HAVE_SETJMP 1
-#define HAVE_NANOSLEEP 1
-#define HAVE_POW 1
-
-#define SDL_AUDIO_DRIVER_DUMMY 1
-#define SDL_AUDIO_DRIVER_OSS 1
-
-#define SDL_INPUT_LINUXEV 1
-#define SDL_INPUT_TSLIB 1
-#define SDL_JOYSTICK_LINUX 1
-#define SDL_HAPTIC_LINUX 1
-
-#define SDL_LOADSO_DLOPEN 1
-
-#define SDL_THREAD_PTHREAD 1
-#define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP 1
-
-#define SDL_TIMER_UNIX 1
-
-#define SDL_VIDEO_DRIVER_DUMMY 1
-#define SDL_VIDEO_DRIVER_PANDORA 1
-#define SDL_VIDEO_RENDER_OGL_ES 1
-#define SDL_VIDEO_OPENGL_ES 1
-
-#endif /* SDL_config_wiz_h_ */

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_copying.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 111 - 12
Source/ThirdParty/SDL/include/SDL_cpuinfo.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -41,37 +41,64 @@
 #else
 #include <intrin.h>
 #ifndef _WIN64
+#ifndef __MMX__
 #define __MMX__
+#endif
+#ifndef __3dNOW__
 #define __3dNOW__
 #endif
+#endif
+#ifndef __SSE__
 #define __SSE__
+#endif
+#ifndef __SSE2__
 #define __SSE2__
+#endif
 #endif /* __clang__ */
 #elif defined(__MINGW64_VERSION_MAJOR)
 #include <intrin.h>
 #else
-#ifdef __ALTIVEC__
-#if HAVE_ALTIVEC_H && !defined(__APPLE_ALTIVEC__)
+/* altivec.h redefining bool causes a number of problems, see bugs 3993 and 4392, so you need to explicitly define SDL_ENABLE_ALTIVEC_H to have it included. */
+#if defined(HAVE_ALTIVEC_H) && defined(__ALTIVEC__) && !defined(__APPLE_ALTIVEC__) && defined(SDL_ENABLE_ALTIVEC_H)
 #include <altivec.h>
-#undef pixel
 #endif
+#if !defined(SDL_DISABLE_ARM_NEON_H)
+#  if defined(__ARM_NEON)
+#    include <arm_neon.h>
+#  elif defined(__WINDOWS__) || defined(__WINRT__)
+/* Visual Studio doesn't define __ARM_ARCH, but _M_ARM (if set, always 7), and _M_ARM64 (if set, always 1). */
+#    if defined(_M_ARM)
+#      include <armintr.h>
+#      include <arm_neon.h>
+#      define __ARM_NEON 1 /* Set __ARM_NEON so that it can be used elsewhere, at compile time */
+#    endif
+#    if defined (_M_ARM64)
+#      include <armintr.h>
+#      include <arm_neon.h>
+#      define __ARM_NEON 1 /* Set __ARM_NEON so that it can be used elsewhere, at compile time */
+#    endif
+#  endif
 #endif
-#ifdef __MMX__
-#include <mmintrin.h>
-#endif
-#ifdef __3dNOW__
+#if defined(__3dNOW__) && !defined(SDL_DISABLE_MM3DNOW_H)
 #include <mm3dnow.h>
 #endif
-#ifdef __SSE__
+#if defined(HAVE_IMMINTRIN_H) && !defined(SDL_DISABLE_IMMINTRIN_H)
+#include <immintrin.h>
+#else
+#if defined(__MMX__) && !defined(SDL_DISABLE_MMINTRIN_H)
+#include <mmintrin.h>
+#endif
+#if defined(__SSE__) && !defined(SDL_DISABLE_XMMINTRIN_H)
 #include <xmmintrin.h>
 #endif
-#ifdef __SSE2__
+#if defined(__SSE2__) && !defined(SDL_DISABLE_EMMINTRIN_H)
 #include <emmintrin.h>
 #endif
-#ifdef __SSE3__
+#if defined(__SSE3__) && !defined(SDL_DISABLE_PMMINTRIN_H)
 #include <pmmintrin.h>
 #endif
-#endif
+#endif /* HAVE_IMMINTRIN_H */
+#endif /* compiler version */
 
 #include "begin_code.h"
 /* Set up for C function definitions, even when using C++ */
@@ -154,6 +181,11 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX(void);
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX2(void);
 
+/**
+ *  This function returns true if the CPU has AVX-512F (foundation) features.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX512F(void);
+
 /**
  *  This function returns true if the CPU has NEON (ARM SIMD) features.
  */
@@ -164,6 +196,73 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasNEON(void);
  */
 extern DECLSPEC int SDLCALL SDL_GetSystemRAM(void);
 
+/**
+ * \brief Report the alignment this system needs for SIMD allocations.
+ *
+ * This will return the minimum number of bytes to which a pointer must be
+ *  aligned to be compatible with SIMD instructions on the current machine.
+ *  For example, if the machine supports SSE only, it will return 16, but if
+ *  it supports AVX-512F, it'll return 64 (etc). This only reports values for
+ *  instruction sets SDL knows about, so if your SDL build doesn't have
+ *  SDL_HasAVX512F(), then it might return 16 for the SSE support it sees and
+ *  not 64 for the AVX-512 instructions that exist but SDL doesn't know about.
+ *  Plan accordingly.
+ */
+extern DECLSPEC size_t SDLCALL SDL_SIMDGetAlignment(void);
+
+// Urho3D - bug fix - check if SIMD is supported
+#ifdef __EMSCRIPTEN__
+#define SDL_SIMDAlloc SDL_malloc
+#define SDL_SIMDFree SDL_free
+#else
+/**
+ * \brief Allocate memory in a SIMD-friendly way.
+ *
+ * This will allocate a block of memory that is suitable for use with SIMD
+ *  instructions. Specifically, it will be properly aligned and padded for
+ *  the system's supported vector instructions.
+ *
+ * The memory returned will be padded such that it is safe to read or write
+ *  an incomplete vector at the end of the memory block. This can be useful
+ *  so you don't have to drop back to a scalar fallback at the end of your
+ *  SIMD processing loop to deal with the final elements without overflowing
+ *  the allocated buffer.
+ *
+ * You must free this memory with SDL_FreeSIMD(), not free() or SDL_free()
+ *  or delete[], etc.
+ *
+ * Note that SDL will only deal with SIMD instruction sets it is aware of;
+ *  for example, SDL 2.0.8 knows that SSE wants 16-byte vectors
+ *  (SDL_HasSSE()), and AVX2 wants 32 bytes (SDL_HasAVX2()), but doesn't
+ *  know that AVX-512 wants 64. To be clear: if you can't decide to use an
+ *  instruction set with an SDL_Has*() function, don't use that instruction
+ *  set with memory allocated through here.
+ *
+ * SDL_AllocSIMD(0) will return a non-NULL pointer, assuming the system isn't
+ *  out of memory.
+ *
+ *  \param len The length, in bytes, of the block to allocated. The actual
+ *             allocated block might be larger due to padding, etc.
+ * \return Pointer to newly-allocated block, NULL if out of memory.
+ *
+ * \sa SDL_SIMDAlignment
+ * \sa SDL_SIMDFree
+ */
+extern DECLSPEC void * SDLCALL SDL_SIMDAlloc(const size_t len);
+
+/**
+ * \brief Deallocate memory obtained from SDL_SIMDAlloc
+ *
+ * It is not valid to use this function on a pointer from anything but
+ *  SDL_SIMDAlloc(). It can't be used on pointers from malloc, realloc,
+ *  SDL_malloc, memalign, new[], etc.
+ *
+ * However, SDL_SIMDFree(NULL) is a legal no-op.
+ *
+ * \sa SDL_SIMDAlloc
+ */
+extern DECLSPEC void SDLCALL SDL_SIMDFree(void *ptr);
+#endif
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus

+ 2 - 2
Source/ThirdParty/SDL/include/SDL_egl.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -24,7 +24,7 @@
  *
  *  This is a simple file to encapsulate the EGL API headers.
  */
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) && !defined(__ANDROID__)
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_endian.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_error.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 38 - 1
Source/ThirdParty/SDL/include/SDL_events.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -85,6 +85,9 @@ typedef enum
                                      Called on Android in onResume()
                                 */
 
+    /* Display events */
+    SDL_DISPLAYEVENT   = 0x150,  /**< Display state change */
+
     /* Window events */
     SDL_WINDOWEVENT    = 0x200, /**< Window state change */
     SDL_SYSWMEVENT,             /**< System specific event */
@@ -144,6 +147,9 @@ typedef enum
     SDL_AUDIODEVICEADDED = 0x1100, /**< A new audio device is available */
     SDL_AUDIODEVICEREMOVED,        /**< An audio device has been removed. */
 
+    /* Sensor events */
+    SDL_SENSORUPDATE = 0x1200,     /**< A sensor was updated */
+
     /* Render events */
     SDL_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
     SDL_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */
@@ -168,6 +174,21 @@ typedef struct SDL_CommonEvent
     Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
 } SDL_CommonEvent;
 
+/**
+ *  \brief Display state change event data (event.display.*)
+ */
+typedef struct SDL_DisplayEvent
+{
+    Uint32 type;        /**< ::SDL_DISPLAYEVENT */
+    Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
+    Uint32 display;     /**< The associated display index */
+    Uint8 event;        /**< ::SDL_DisplayEventID */
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint32 data1;       /**< event dependent data */
+} SDL_DisplayEvent;
+
 /**
  *  \brief Window state change event data (event.window.*)
  */
@@ -471,6 +492,17 @@ typedef struct SDL_DropEvent
 } SDL_DropEvent;
 
 
+/**
+ *  \brief Sensor event structure (event.sensor.*)
+ */
+typedef struct SDL_SensorEvent
+{
+    Uint32 type;        /**< ::SDL_SENSORUPDATE */
+    Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
+    Sint32 which;       /**< The instance ID of the sensor */
+    float data[6];      /**< Up to 6 values from the sensor - additional values can be queried using SDL_SensorGetData() */
+} SDL_SensorEvent;
+
 /**
  *  \brief The "quit requested" event
  */
@@ -526,6 +558,7 @@ typedef union SDL_Event
 {
     Uint32 type;                    /**< Event type, shared with all events */
     SDL_CommonEvent common;         /**< Common event data */
+    SDL_DisplayEvent display;       /**< Window event data */
     SDL_WindowEvent window;         /**< Window event data */
     SDL_KeyboardEvent key;          /**< Keyboard event data */
     SDL_TextEditingEvent edit;      /**< Text editing event data */
@@ -542,6 +575,7 @@ typedef union SDL_Event
     SDL_ControllerButtonEvent cbutton;  /**< Game Controller button event data */
     SDL_ControllerDeviceEvent cdevice;  /**< Game Controller device event data */
     SDL_AudioDeviceEvent adevice;   /**< Audio device event data */
+    SDL_SensorEvent sensor;         /**< Sensor event data */
     SDL_QuitEvent quit;             /**< Quit request event data */
     SDL_UserEvent user;             /**< Custom event data */
     SDL_SysWMEvent syswm;           /**< System dependent window event data */
@@ -560,6 +594,9 @@ typedef union SDL_Event
     Uint8 padding[56];
 } SDL_Event;
 
+/* Make sure we haven't broken binary compatibility */
+SDL_COMPILE_TIME_ASSERT(SDL_Event, sizeof(SDL_Event) == 56);
+
 
 /* Function prototypes */
 

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_filesystem.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 29 - 1
Source/ThirdParty/SDL/include/SDL_gamecontroller.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -175,6 +175,14 @@ extern DECLSPEC SDL_bool SDLCALL SDL_IsGameController(int joystick_index);
  */
 extern DECLSPEC const char *SDLCALL SDL_GameControllerNameForIndex(int joystick_index);
 
+/**
+ *  Get the mapping of a game controller.
+ *  This can be called before any controllers are opened.
+ *
+ *  \return the mapping string.  Must be freed with SDL_free().  Returns NULL if no mapping is available
+ */
+extern DECLSPEC char *SDLCALL SDL_GameControllerMappingForDeviceIndex(int joystick_index);
+
 /**
  *  Open a game controller for use.
  *  The index passed as an argument refers to the N'th game controller on the system.
@@ -196,6 +204,13 @@ extern DECLSPEC SDL_GameController *SDLCALL SDL_GameControllerFromInstanceID(SDL
  */
 extern DECLSPEC const char *SDLCALL SDL_GameControllerName(SDL_GameController *gamecontroller);
 
+/**
+ *  Get the player index of an opened game controller, or -1 if it's not available
+ *
+ *  For XInput controllers this returns the XInput user index.
+ */
+extern DECLSPEC int SDLCALL SDL_GameControllerGetPlayerIndex(SDL_GameController *gamecontroller);
+
 /**
  *  Get the USB vendor ID of an opened controller, if available.
  *  If the vendor ID isn't available this function returns 0.
@@ -345,6 +360,19 @@ SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller,
 extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *gamecontroller,
                                                           SDL_GameControllerButton button);
 
+/**
+ *  Trigger a rumble effect
+ *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ *  \param gamecontroller The controller to vibrate
+ *  \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
+ *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
+ *  \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ *  \return 0, or -1 if rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
 /**
  *  Close a controller previously opened with SDL_GameControllerOpen().
  */

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_gesture.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 15 - 4
Source/ThirdParty/SDL/include/SDL_haptic.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -117,6 +117,17 @@
 extern "C" {
 #endif /* __cplusplus */
 
+/* FIXME: For SDL 2.1, adjust all the magnitude variables to be Uint16 (0xFFFF).
+ *
+ * At the moment the magnitude variables are mixed between signed/unsigned, and
+ * it is also not made clear that ALL of those variables expect a max of 0x7FFF.
+ *
+ * Some platforms may have higher precision than that (Linux FF, Windows XInput)
+ * so we should fix the inconsistency in favor of higher possible precision,
+ * adjusting for platforms that use different scales.
+ * -flibit
+ */
+
 /**
  *  \typedef SDL_Haptic
  *
@@ -656,8 +667,8 @@ typedef struct SDL_HapticRamp
  * This struct is exclusively for the ::SDL_HAPTIC_LEFTRIGHT effect.
  *
  * The Left/Right effect is used to explicitly control the large and small
- * motors, commonly found in modern game controllers. One motor is high
- * frequency, the other is low frequency.
+ * motors, commonly found in modern game controllers. The small (right) motor
+ * is high frequency, and the large (left) motor is low frequency.
  *
  * \sa SDL_HAPTIC_LEFTRIGHT
  * \sa SDL_HapticEffect
@@ -668,7 +679,7 @@ typedef struct SDL_HapticLeftRight
     Uint16 type;            /**< ::SDL_HAPTIC_LEFTRIGHT */
 
     /* Replay */
-    Uint32 length;          /**< Duration of the effect. */
+    Uint32 length;          /**< Duration of the effect in milliseconds. */
 
     /* Rumble */
     Uint16 large_magnitude; /**< Control of the large controller motor. */

+ 319 - 12
Source/ThirdParty/SDL/include/SDL_hints.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -76,6 +76,7 @@ extern "C" {
  *    "opengl"
  *    "opengles2"
  *    "opengles"
+ *    "metal"
  *    "software"
  *
  *  The default varies by platform, but it's the first one in the list that
@@ -210,6 +211,18 @@ extern "C" {
  */
 #define SDL_HINT_VIDEO_X11_NET_WM_PING      "SDL_VIDEO_X11_NET_WM_PING"
 
+/**
+ * \brief A variable controlling whether the X11 _NET_WM_BYPASS_COMPOSITOR hint should be used.
+ * 
+ * This variable can be set to the following values:
+ * "0" - Disable _NET_WM_BYPASS_COMPOSITOR
+ * "1" - Enable _NET_WM_BYPASS_COMPOSITOR
+ * 
+ * By default SDL will use _NET_WM_BYPASS_COMPOSITOR
+ * 
+ */
+#define SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"
+
 /**
  *  \brief  A variable controlling whether the window frame and title bar are interactive when the cursor is hidden 
  *
@@ -249,6 +262,16 @@ extern "C" {
  */
 #define SDL_HINT_GRAB_KEYBOARD              "SDL_GRAB_KEYBOARD"
 
+/**
+ *  \brief  A variable setting the double click time, in milliseconds.
+ */
+#define SDL_HINT_MOUSE_DOUBLE_CLICK_TIME    "SDL_MOUSE_DOUBLE_CLICK_TIME"
+
+/**
+ *  \brief  A variable setting the double click radius, in pixels.
+ */
+#define SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS    "SDL_MOUSE_DOUBLE_CLICK_RADIUS"
+
 /**
  *  \brief  A variable setting the speed scale for mouse motion, in floating point, when the mouse is not in relative mode
  */
@@ -292,6 +315,16 @@ extern "C" {
  */
 #define SDL_HINT_TOUCH_MOUSE_EVENTS    "SDL_TOUCH_MOUSE_EVENTS"
 
+/**
+ *  \brief  A variable controlling whether mouse events should generate synthetic touch events
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Mouse events will not generate touch events (default for desktop platforms)
+ *    "1"       - Mouse events will generate touch events (default for mobile platforms, such as Android and iOS)
+ */
+
+#define SDL_HINT_MOUSE_TOUCH_EVENTS    "SDL_MOUSE_TOUCH_EVENTS"
+
 /**
  *  \brief Minimize your SDL_Window if it loses key focus when in fullscreen mode. Defaults to true.
  *
@@ -316,7 +349,7 @@ extern "C" {
 #define SDL_HINT_IDLE_TIMER_DISABLED "SDL_IOS_IDLE_TIMER_DISABLED"
 
 /**
- *  \brief  A variable controlling which orientations are allowed on iOS.
+ *  \brief  A variable controlling which orientations are allowed on iOS/Android.
  *
  *  In some circumstances it is necessary to be able to explicitly control
  *  which UI orientations are allowed.
@@ -354,17 +387,37 @@ extern "C" {
  */
 #define SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION"
 
+/**
+ * \brief  A variable controlling whether the home indicator bar on iPhone X
+ *         should be hidden.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - The indicator bar is not hidden (default for windowed applications)
+ *    "1"       - The indicator bar is hidden and is shown when the screen is touched (useful for movie playback applications)
+ *    "2"       - The indicator bar is dim and the first swipe makes it visible and the second swipe performs the "home" action (default for fullscreen applications)
+ */
+#define SDL_HINT_IOS_HIDE_HOME_INDICATOR "SDL_IOS_HIDE_HOME_INDICATOR"
+
 /**
  *  \brief  A variable controlling whether the Android / iOS built-in
- *  accelerometer should be listed as a joystick device, rather than listing
- *  actual joysticks only.
+ *  accelerometer should be listed as a joystick device.
  *
  *  This variable can be set to the following values:
- *    "0"       - List only real joysticks and accept input from them
- *    "1"       - List real joysticks along with the accelerometer as if it were a 3 axis joystick (the default).
+ *    "0"       - The accelerometer is not listed as a joystick
+ *    "1"       - The accelerometer is available as a 3 axis joystick (the default).
  */
 #define SDL_HINT_ACCELEROMETER_AS_JOYSTICK "SDL_ACCELEROMETER_AS_JOYSTICK"
 
+/**
+ *  \brief  A variable controlling whether the Android / tvOS remotes
+ *  should be listed as joystick devices, instead of sending keyboard events.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Remotes send enter/escape/arrow key events
+ *    "1"       - Remotes are available as 2 axis, 2 button joysticks (the default).
+ */
+#define SDL_HINT_TV_REMOTE_AS_JOYSTICK "SDL_TV_REMOTE_AS_JOYSTICK"
+
 /**
  *  \brief  A variable that lets you disable the detection and use of Xinput gamepad devices
  *
@@ -393,6 +446,16 @@ extern "C" {
  */
 #define SDL_HINT_GAMECONTROLLERCONFIG "SDL_GAMECONTROLLERCONFIG"
 
+/**
+ *  \brief  A variable that lets you provide a file with extra gamecontroller db entries.
+ *
+ *  The file should contain lines of gamecontroller config data, see SDL_gamecontroller.h
+ *
+ *  This hint must be set before calling SDL_Init(SDL_INIT_GAMECONTROLLER)
+ *  You can update mappings after the system is initialized with SDL_GameControllerMappingForGUID() and SDL_GameControllerAddMapping()
+ */
+#define SDL_HINT_GAMECONTROLLERCONFIG_FILE "SDL_GAMECONTROLLERCONFIG_FILE"
+
 /**
  *  \brief  A variable containing a list of devices to skip when scanning for game controllers.
  *
@@ -432,6 +495,88 @@ extern "C" {
  */
 #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
 
+/**
+ *  \brief  A variable controlling whether the HIDAPI joystick drivers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI drivers are not used
+ *    "1"       - HIDAPI drivers are used (the default)
+ *
+ *  This variable is the default for all drivers, but can be overridden by the hints for specific drivers below.
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI "SDL_JOYSTICK_HIDAPI"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for PS4 controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_PS4 "SDL_JOYSTICK_HIDAPI_PS4"
+
+/**
+ *  \brief  A variable controlling whether extended input reports should be used for PS4 controllers when using the HIDAPI driver.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - extended reports are not enabled (the default)
+ *    "1"       - extended reports
+ *
+ *  Extended input reports allow rumble on Bluetooth PS4 controllers, but
+ *  break DirectInput handling for applications that don't use SDL.
+ *
+ *  Once extended reports are enabled, they can not be disabled without
+ *  power cycling the controller.
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for Steam Controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_STEAM "SDL_JOYSTICK_HIDAPI_STEAM"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for Nintendo Switch controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH "SDL_JOYSTICK_HIDAPI_SWITCH"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for XBox controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_XBOX   "SDL_JOYSTICK_HIDAPI_XBOX"
+
+/**
+ *  \brief  A variable that controls whether Steam Controllers should be exposed using the SDL joystick and game controller APIs
+ *
+ *  The variable can be set to the following values:
+ *    "0"       - Do not scan for Steam Controllers
+ *    "1"       - Scan for Steam Controllers (the default)
+ *
+ *  The default value is "1".  This hint must be set before initializing the joystick subsystem.
+ */
+#define SDL_HINT_ENABLE_STEAM_CONTROLLERS "SDL_ENABLE_STEAM_CONTROLLERS"
+
+
 /**
  *  \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
  *      This is a debugging aid for developers and not expected to be used by end users. The default is "1"
@@ -494,6 +639,10 @@ extern "C" {
 *  This is specially useful if you build SDL against a non glibc libc library (such as musl) which
 *  provides a relatively small default thread stack size (a few kilobytes versus the default 8MB glibc uses).
 *  Support for this hint is currently available only in the pthread, Windows, and PSP backend.
+*
+*  Instead of this hint, in 2.0.9 and later, you can use
+*  SDL_CreateThreadWithStackSize(). This hint only works with the classic
+*  SDL_CreateThread().
 */
 #define SDL_HINT_THREAD_STACK_SIZE              "SDL_THREAD_STACK_SIZE"
 
@@ -707,17 +856,45 @@ extern "C" {
  */
 #define SDL_HINT_IME_INTERNAL_EDITING "SDL_IME_INTERNAL_EDITING"
 
+/**
+ * \brief A variable to control whether we trap the Android back button to handle it manually.
+ *        This is necessary for the right mouse button to work on some Android devices, or
+ *        to be able to trap the back button for use in your code reliably.  If set to true,
+ *        the back button will show up as an SDL_KEYDOWN / SDL_KEYUP pair with a keycode of 
+ *        SDL_SCANCODE_AC_BACK.
+ *
+ * The variable can be set to the following values:
+ *   "0"       - Back button will be handled as usual for system. (default)
+ *   "1"       - Back button will be trapped, allowing you to handle the key press
+ *               manually.  (This will also let right mouse click work on systems 
+ *               where the right mouse button functions as back.)
+ *
+ * The value of this hint is used at runtime, so it can be changed at any time.
+ */
+#define SDL_HINT_ANDROID_TRAP_BACK_BUTTON "SDL_ANDROID_TRAP_BACK_BUTTON"
+
+/**
+ * \brief A variable to control whether the event loop will block itself when the app is paused.
+ *
+ * The variable can be set to the following values:
+ *   "0"       - Non blocking.
+ *   "1"       - Blocking. (default)
+ *
+ * The value should be set before SDL is initialized.
+ */
+#define SDL_HINT_ANDROID_BLOCK_ON_PAUSE "SDL_ANDROID_BLOCK_ON_PAUSE"
+
  /**
- * \brief A variable to control whether mouse and touch events are to be treated together or separately
+ * \brief A variable to control whether the return key on the soft keyboard
+ *        should hide the soft keyboard on Android and iOS.
  *
  * The variable can be set to the following values:
- *   "0"       - Mouse events will be handled as touch events, and touch will raise fake mouse
- *               events. This is the behaviour of SDL <= 2.0.3. (default)
- *   "1"       - Mouse events will be handled separately from pure touch events.
+ *   "0"       - The return key will be handled as a key event. This is the behaviour of SDL <= 2.0.3. (default)
+ *   "1"       - The return key will hide the keyboard.
  *
  * The value of this hint is used at runtime, so it can be changed at any time.
  */
-#define SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH "SDL_ANDROID_SEPARATE_MOUSE_AND_TOUCH"
+#define SDL_HINT_RETURN_KEY_HIDES_IME "SDL_RETURN_KEY_HIDES_IME"
 
 /**
  *  \brief override the binding element for keyboard inputs for Emscripten builds
@@ -752,7 +929,7 @@ extern "C" {
  *   "0"       - SDL will generate a window-close event when it sees Alt+F4.
  *   "1"       - SDL will only do normal key handling for Alt+F4.
  */
-#define SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4	"SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"
+#define SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 "SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"
 
 /**
  *  \brief Prevent SDL from using version 4 of the bitmap header when saving BMPs.
@@ -797,6 +974,24 @@ extern "C" {
  */
 #define SDL_HINT_RPI_VIDEO_LAYER           "SDL_RPI_VIDEO_LAYER"
 
+/**
+ * \brief Tell the video driver that we only want a double buffer.
+ *
+ * By default, most lowlevel 2D APIs will use a triple buffer scheme that 
+ * wastes no CPU time on waiting for vsync after issuing a flip, but
+ * introduces a frame of latency. On the other hand, using a double buffer
+ * scheme instead is recommended for cases where low latency is an important
+ * factor because we save a whole frame of latency.
+ * We do so by waiting for vsync immediately after issuing a flip, usually just
+ * after eglSwapBuffers call in the backend's *_SwapWindow function.
+ *
+ * Since it's driver-specific, it's only supported where possible and
+ * implemented. Currently supported the following drivers:
+ * - KMSDRM (kmsdrm)
+ * - Raspberry Pi (raspberrypi)
+ */
+#define SDL_HINT_VIDEO_DOUBLE_BUFFER      "SDL_VIDEO_DOUBLE_BUFFER"
+
 /**
  *  \brief  A variable controlling what driver to use for OpenGL ES contexts.
  *
@@ -867,6 +1062,118 @@ extern "C" {
  */
 #define SDL_HINT_AUDIO_CATEGORY   "SDL_AUDIO_CATEGORY"
 
+/**
+ *  \brief  A variable controlling whether the 2D render API is compatible or efficient.
+ *
+ *  This variable can be set to the following values:
+ *
+ *    "0"     - Don't use batching to make rendering more efficient.
+ *    "1"     - Use batching, but might cause problems if app makes its own direct OpenGL calls.
+ *
+ *  Up to SDL 2.0.9, the render API would draw immediately when requested. Now
+ *  it batches up draw requests and sends them all to the GPU only when forced
+ *  to (during SDL_RenderPresent, when changing render targets, by updating a
+ *  texture that the batch needs, etc). This is significantly more efficient,
+ *  but it can cause problems for apps that expect to render on top of the
+ *  render API's output. As such, SDL will disable batching if a specific
+ *  render backend is requested (since this might indicate that the app is
+ *  planning to use the underlying graphics API directly). This hint can
+ *  be used to explicitly request batching in this instance. It is a contract
+ *  that you will either never use the underlying graphics API directly, or
+ *  if you do, you will call SDL_RenderFlush() before you do so any current
+ *  batch goes to the GPU before your work begins. Not following this contract
+ *  will result in undefined behavior.
+ */
+#define SDL_HINT_RENDER_BATCHING  "SDL_RENDER_BATCHING"
+
+
+/**
+ *  \brief  A variable controlling whether SDL logs all events pushed onto its internal queue.
+ *
+ *  This variable can be set to the following values:
+ *
+ *    "0"     - Don't log any events (default)
+ *    "1"     - Log all events except mouse and finger motion, which are pretty spammy.
+ *    "2"     - Log all events.
+ *
+ *  This is generally meant to be used to debug SDL itself, but can be useful
+ *  for application developers that need better visibility into what is going
+ *  on in the event queue. Logged events are sent through SDL_Log(), which
+ *  means by default they appear on stdout on most platforms or maybe
+ *  OutputDebugString() on Windows, and can be funneled by the app with
+ *  SDL_LogSetOutputFunction(), etc.
+ *
+ *  This hint can be toggled on and off at runtime, if you only need to log
+ *  events for a small subset of program execution.
+ */
+#define SDL_HINT_EVENT_LOGGING   "SDL_EVENT_LOGGING"
+
+
+
+/**
+ *  \brief  Controls how the size of the RIFF chunk affects the loading of a WAVE file.
+ *
+ *  The size of the RIFF chunk (which includes all the sub-chunks of the WAVE
+ *  file) is not always reliable. In case the size is wrong, it's possible to
+ *  just ignore it and step through the chunks until a fixed limit is reached.
+ *
+ *  Note that files that have trailing data unrelated to the WAVE file or
+ *  corrupt files may slow down the loading process without a reliable boundary.
+ *  By default, SDL stops after 10000 chunks to prevent wasting time. Use the
+ *  environment variable SDL_WAVE_CHUNK_LIMIT to adjust this value.
+ *
+ *  This variable can be set to the following values:
+ *
+ *    "force"        - Always use the RIFF chunk size as a boundary for the chunk search
+ *    "ignorezero"   - Like "force", but a zero size searches up to 4 GiB (default)
+ *    "ignore"       - Ignore the RIFF chunk size and always search up to 4 GiB
+ *    "maximum"      - Search for chunks until the end of file (not recommended)
+ */
+#define SDL_HINT_WAVE_RIFF_CHUNK_SIZE   "SDL_WAVE_RIFF_CHUNK_SIZE"
+
+/**
+ *  \brief  Controls how a truncated WAVE file is handled.
+ *
+ *  A WAVE file is considered truncated if any of the chunks are incomplete or
+ *  the data chunk size is not a multiple of the block size. By default, SDL
+ *  decodes until the first incomplete block, as most applications seem to do.
+ *
+ *  This variable can be set to the following values:
+ *
+ *    "verystrict" - Raise an error if the file is truncated
+ *    "strict"     - Like "verystrict", but the size of the RIFF chunk is ignored
+ *    "dropframe"  - Decode until the first incomplete sample frame
+ *    "dropblock"  - Decode until the first incomplete block (default)
+ */
+#define SDL_HINT_WAVE_TRUNCATION   "SDL_WAVE_TRUNCATION"
+
+/**
+ *  \brief  Controls how the fact chunk affects the loading of a WAVE file.
+ *
+ *  The fact chunk stores information about the number of samples of a WAVE
+ *  file. The Standards Update from Microsoft notes that this value can be used
+ *  to 'determine the length of the data in seconds'. This is especially useful
+ *  for compressed formats (for which this is a mandatory chunk) if they produce
+ *  multiple sample frames per block and truncating the block is not allowed.
+ *  The fact chunk can exactly specify how many sample frames there should be
+ *  in this case.
+ *
+ *  Unfortunately, most application seem to ignore the fact chunk and so SDL
+ *  ignores it by default as well.
+ *
+ *  This variable can be set to the following values:
+ *
+ *    "truncate"    - Use the number of samples to truncate the wave data if
+ *                    the fact chunk is present and valid
+ *    "strict"      - Like "truncate", but raise an error if the fact chunk
+ *                    is invalid, not present for non-PCM formats, or if the
+ *                    data chunk doesn't have that many samples
+ *    "ignorezero"  - Like "truncate", but ignore fact chunk if the number of
+ *                    samples is zero
+ *    "ignore"      - Ignore fact chunk entirely (default)
+ */
+#define SDL_HINT_WAVE_FACT_CHUNK   "SDL_WAVE_FACT_CHUNK"
+
 /**
  *  \brief  An enumeration of hint priorities
  */

+ 31 - 5
Source/ThirdParty/SDL/include/SDL_joystick.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -97,10 +97,10 @@ typedef enum
 typedef enum
 {
     SDL_JOYSTICK_POWER_UNKNOWN = -1,
-    SDL_JOYSTICK_POWER_EMPTY,
-    SDL_JOYSTICK_POWER_LOW,
-    SDL_JOYSTICK_POWER_MEDIUM,
-    SDL_JOYSTICK_POWER_FULL,
+    SDL_JOYSTICK_POWER_EMPTY,   /* <= 5% */
+    SDL_JOYSTICK_POWER_LOW,     /* <= 20% */
+    SDL_JOYSTICK_POWER_MEDIUM,  /* <= 70% */
+    SDL_JOYSTICK_POWER_FULL,    /* <= 100% */
     SDL_JOYSTICK_POWER_WIRED,
     SDL_JOYSTICK_POWER_MAX
 } SDL_JoystickPowerLevel;
@@ -132,6 +132,12 @@ extern DECLSPEC int SDLCALL SDL_NumJoysticks(void);
  */
 extern DECLSPEC const char *SDLCALL SDL_JoystickNameForIndex(int device_index);
 
+/**
+ *  Get the player index of a joystick, or -1 if it's not available
+ *  This can be called before any joysticks are opened.
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickGetDevicePlayerIndex(int device_index);
+
 /**
  *  Return the GUID for the joystick at this index
  *  This can be called before any joysticks are opened.
@@ -194,6 +200,13 @@ extern DECLSPEC SDL_Joystick *SDLCALL SDL_JoystickFromInstanceID(SDL_JoystickID
  */
 extern DECLSPEC const char *SDLCALL SDL_JoystickName(SDL_Joystick * joystick);
 
+/**
+ *  Get the player index of an opened joystick, or -1 if it's not available
+ *
+ *  For XInput controllers this returns the XInput user index.
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickGetPlayerIndex(SDL_Joystick * joystick);
+
 /**
  *  Return the GUID for this opened joystick
  */
@@ -361,6 +374,19 @@ extern DECLSPEC int SDLCALL SDL_JoystickGetBall(SDL_Joystick * joystick,
 extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick * joystick,
                                                     int button);
 
+/**
+ *  Trigger a rumble effect
+ *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ *  \param joystick The joystick to vibrate
+ *  \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
+ *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
+ *  \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ *  \return 0, or -1 if rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
 /**
  *  Close a joystick previously opened with SDL_JoystickOpen().
  */

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_keyboard.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_keycode.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_loadso.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_log.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 35 - 16
Source/ThirdParty/SDL/include/SDL_main.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -55,6 +55,10 @@
 /* On iOS SDL provides a main function that creates an application delegate
    and starts the iOS application run loop.
 
+   If you link with SDL dynamically on iOS, the main function can't be in a
+   shared library, so you need to link with libSDLmain.a, which includes a
+   stub main function that calls into the shared library to start execution.
+
    See src/video/uikit/SDL_uikitappdelegate.m for more details.
  */
 #define SDL_MAIN_NEEDED
@@ -63,10 +67,13 @@
 /* On Android SDL provides a Java class in SDLActivity.java that is the
    main activity entry point.
 
-   See README-android.md for more details on extending that class.
+   See docs/README-android.md for more details on extending that class.
  */
 #define SDL_MAIN_NEEDED
 
+/* We need to export SDL_main so it can be launched from Java */
+#define SDLMAIN_DECLSPEC    DECLSPEC
+
 #elif defined(__NACL__)
 /* On NACL we use ppapi_simple to set up the application helper code,
    then wait for the first PSE_INSTANCE_DIDCHANGEVIEW event before 
@@ -79,11 +86,9 @@
 #endif
 #endif /* SDL_MAIN_HANDLED */
 
-#ifdef __cplusplus
-#define C_LINKAGE   "C"
-#else
-#define C_LINKAGE
-#endif /* __cplusplus */
+#ifndef SDLMAIN_DECLSPEC
+#define SDLMAIN_DECLSPEC
+#endif
 
 /**
  *  \file SDL_main.h
@@ -104,17 +109,18 @@
 #define main    SDL_main
 #endif
 
-/**
- *  The prototype for the application's main() function
- */
-extern C_LINKAGE DECLSPEC int SDL_main(int argc, char *argv[]);
-
-
 #include "begin_code.h"
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/**
+ *  The prototype for the application's main() function
+ */
+typedef int (*SDL_main_func)(int argc, char *argv[]);
+extern SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[]);
+
+
 /**
  *  This is called by the real SDL main function to let the rest of the
  *  library know that initialization was done properly.
@@ -129,8 +135,7 @@ extern DECLSPEC void SDLCALL SDL_SetMainReady(void);
 /**
  *  This can be called to set the application class at startup
  */
-extern DECLSPEC int SDLCALL SDL_RegisterApp(char *name, Uint32 style,
-                                            void *hInst);
+extern DECLSPEC int SDLCALL SDL_RegisterApp(char *name, Uint32 style, void *hInst);
 extern DECLSPEC void SDLCALL SDL_UnregisterApp(void);
 
 #endif /* __WIN32__ */
@@ -146,10 +151,24 @@ extern DECLSPEC void SDLCALL SDL_UnregisterApp(void);
  *  \return 0 on success, -1 on failure.  On failure, use SDL_GetError to retrieve more
  *      information on the failure.
  */
-extern DECLSPEC int SDLCALL SDL_WinRTRunApp(int (*mainFunction)(int, char **), void * reserved);
+extern DECLSPEC int SDLCALL SDL_WinRTRunApp(SDL_main_func mainFunction, void * reserved);
 
 #endif /* __WINRT__ */
 
+#if defined(__IPHONEOS__)
+
+/**
+ *  \brief Initializes and launches an SDL application.
+ *
+ *  \param argc The argc parameter from the application's main() function
+ *  \param argv The argv parameter from the application's main() function
+ *  \param mainFunction The SDL app's C-style main().
+ *  \return the return value from mainFunction
+ */
+extern DECLSPEC int SDLCALL SDL_UIKitRunApp(int argc, char *argv[], SDL_main_func mainFunction);
+
+#endif /* __IPHONEOS__ */
+
 
 #ifdef __cplusplus
 }

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_messagebox.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_mouse.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_mutex.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_name.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_opengl.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_opengles.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_opengles2.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 6 - 4
Source/ThirdParty/SDL/include/SDL_pixels.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -168,7 +168,7 @@ enum
     ((format) && (SDL_PIXELFLAG(format) != 1))
 
 /* Note: If you modify this list, update SDL_GetPixelFormatName() */
-enum
+typedef enum
 {
     SDL_PIXELFORMAT_UNKNOWN,
     SDL_PIXELFORMAT_INDEX1LSB =
@@ -287,8 +287,10 @@ enum
     SDL_PIXELFORMAT_NV12 =      /**< Planar mode: Y + U/V interleaved  (2 planes) */
         SDL_DEFINE_PIXELFOURCC('N', 'V', '1', '2'),
     SDL_PIXELFORMAT_NV21 =      /**< Planar mode: Y + V/U interleaved  (2 planes) */
-        SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1')
-};
+        SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1'),
+    SDL_PIXELFORMAT_EXTERNAL_OES =      /**< Android video texture format */
+        SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ')
+} SDL_PixelFormatEnum;
 
 typedef struct SDL_Color
 {

+ 7 - 2
Source/ThirdParty/SDL/include/SDL_platform.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -121,7 +121,12 @@
 #if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
 /* Try to find out if we're compiling for WinRT or non-WinRT */
 #if defined(_MSC_VER) && defined(__has_include)
-#define HAVE_WINAPIFAMILY_H __has_include(<winapifamily.h>)
+#if __has_include(<winapifamily.h>)
+#define HAVE_WINAPIFAMILY_H 1
+#else
+#define HAVE_WINAPIFAMILY_H 0
+#endif
+
 /* If _USING_V110_SDK71_ is defined it means we are using the Windows XP toolset. */
 #elif defined(_MSC_VER) && (_MSC_VER >= 1700 && !_USING_V110_SDK71_)    /* _MSC_VER == 1700 for Visual Studio 2012 */
 #define HAVE_WINAPIFAMILY_H 1

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_power.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_quit.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 29 - 3
Source/ThirdParty/SDL/include/SDL_rect.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -40,7 +40,7 @@ extern "C" {
 #endif
 
 /**
- *  \brief  The structure that defines a point
+ *  \brief  The structure that defines a point (integer)
  *
  *  \sa SDL_EnclosePoints
  *  \sa SDL_PointInRect
@@ -52,7 +52,20 @@ typedef struct SDL_Point
 } SDL_Point;
 
 /**
- *  \brief A rectangle, with the origin at the upper left.
+ *  \brief  The structure that defines a point (floating point)
+ *
+ *  \sa SDL_EnclosePoints
+ *  \sa SDL_PointInRect
+ */
+typedef struct SDL_FPoint
+{
+    float x;
+    float y;
+} SDL_FPoint;
+
+
+/**
+ *  \brief A rectangle, with the origin at the upper left (integer).
  *
  *  \sa SDL_RectEmpty
  *  \sa SDL_RectEquals
@@ -67,6 +80,19 @@ typedef struct SDL_Rect
     int w, h;
 } SDL_Rect;
 
+
+/**
+ *  \brief A rectangle, with the origin at the upper left (floating point).
+ */
+typedef struct SDL_FRect
+{
+    float x;
+    float y;
+    float w;
+    float h;
+} SDL_FRect;
+
+
 /**
  *  \brief Returns true if point resides inside a rectangle.
  */

+ 189 - 1
Source/ThirdParty/SDL/include/SDL_render.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -835,6 +835,148 @@ extern DECLSPEC int SDLCALL SDL_RenderCopyEx(SDL_Renderer * renderer,
                                            const SDL_Point *center,
                                            const SDL_RendererFlip flip);
 
+
+/**
+ *  \brief Draw a point on the current rendering target.
+ *
+ *  \param renderer The renderer which should draw a point.
+ *  \param x The x coordinate of the point.
+ *  \param y The y coordinate of the point.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderDrawPointF(SDL_Renderer * renderer,
+                                                 float x, float y);
+
+/**
+ *  \brief Draw multiple points on the current rendering target.
+ *
+ *  \param renderer The renderer which should draw multiple points.
+ *  \param points The points to draw
+ *  \param count The number of points to draw
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderDrawPointsF(SDL_Renderer * renderer,
+                                                  const SDL_FPoint * points,
+                                                  int count);
+
+/**
+ *  \brief Draw a line on the current rendering target.
+ *
+ *  \param renderer The renderer which should draw a line.
+ *  \param x1 The x coordinate of the start point.
+ *  \param y1 The y coordinate of the start point.
+ *  \param x2 The x coordinate of the end point.
+ *  \param y2 The y coordinate of the end point.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderDrawLineF(SDL_Renderer * renderer,
+                                                float x1, float y1, float x2, float y2);
+
+/**
+ *  \brief Draw a series of connected lines on the current rendering target.
+ *
+ *  \param renderer The renderer which should draw multiple lines.
+ *  \param points The points along the lines
+ *  \param count The number of points, drawing count-1 lines
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderDrawLinesF(SDL_Renderer * renderer,
+                                                const SDL_FPoint * points,
+                                                int count);
+
+/**
+ *  \brief Draw a rectangle on the current rendering target.
+ *
+ *  \param renderer The renderer which should draw a rectangle.
+ *  \param rect A pointer to the destination rectangle, or NULL to outline the entire rendering target.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderDrawRectF(SDL_Renderer * renderer,
+                                               const SDL_FRect * rect);
+
+/**
+ *  \brief Draw some number of rectangles on the current rendering target.
+ *
+ *  \param renderer The renderer which should draw multiple rectangles.
+ *  \param rects A pointer to an array of destination rectangles.
+ *  \param count The number of rectangles.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderDrawRectsF(SDL_Renderer * renderer,
+                                                 const SDL_FRect * rects,
+                                                 int count);
+
+/**
+ *  \brief Fill a rectangle on the current rendering target with the drawing color.
+ *
+ *  \param renderer The renderer which should fill a rectangle.
+ *  \param rect A pointer to the destination rectangle, or NULL for the entire
+ *              rendering target.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderFillRectF(SDL_Renderer * renderer,
+                                                const SDL_FRect * rect);
+
+/**
+ *  \brief Fill some number of rectangles on the current rendering target with the drawing color.
+ *
+ *  \param renderer The renderer which should fill multiple rectangles.
+ *  \param rects A pointer to an array of destination rectangles.
+ *  \param count The number of rectangles.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderFillRectsF(SDL_Renderer * renderer,
+                                                 const SDL_FRect * rects,
+                                                 int count);
+
+/**
+ *  \brief Copy a portion of the texture to the current rendering target.
+ *
+ *  \param renderer The renderer which should copy parts of a texture.
+ *  \param texture The source texture.
+ *  \param srcrect   A pointer to the source rectangle, or NULL for the entire
+ *                   texture.
+ *  \param dstrect   A pointer to the destination rectangle, or NULL for the
+ *                   entire rendering target.
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderCopyF(SDL_Renderer * renderer,
+                                            SDL_Texture * texture,
+                                            const SDL_Rect * srcrect,
+                                            const SDL_FRect * dstrect);
+
+/**
+ *  \brief Copy a portion of the source texture to the current rendering target, rotating it by angle around the given center
+ *
+ *  \param renderer The renderer which should copy parts of a texture.
+ *  \param texture The source texture.
+ *  \param srcrect   A pointer to the source rectangle, or NULL for the entire
+ *                   texture.
+ *  \param dstrect   A pointer to the destination rectangle, or NULL for the
+ *                   entire rendering target.
+ *  \param angle    An angle in degrees that indicates the rotation that will be applied to dstrect, rotating it in a clockwise direction
+ *  \param center   A pointer to a point indicating the point around which dstrect will be rotated (if NULL, rotation will be done around dstrect.w/2, dstrect.h/2).
+ *  \param flip     An SDL_RendererFlip value stating which flipping actions should be performed on the texture
+ *
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderCopyExF(SDL_Renderer * renderer,
+                                            SDL_Texture * texture,
+                                            const SDL_Rect * srcrect,
+                                            const SDL_FRect * dstrect,
+                                            const double angle,
+                                            const SDL_FPoint *center,
+                                            const SDL_RendererFlip flip);
+
 /**
  *  \brief Read pixels from the current rendering target.
  *
@@ -876,6 +1018,31 @@ extern DECLSPEC void SDLCALL SDL_DestroyTexture(SDL_Texture * texture);
  */
 extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer * renderer);
 
+/**
+ *  \brief Force the rendering context to flush any pending commands to the
+ *         underlying rendering API.
+ *
+ *  You do not need to (and in fact, shouldn't) call this function unless
+ *  you are planning to call into OpenGL/Direct3D/Metal/whatever directly
+ *  in addition to using an SDL_Renderer.
+ *
+ *  This is for a very-specific case: if you are using SDL's render API,
+ *  you asked for a specific renderer backend (OpenGL, Direct3D, etc),
+ *  you set SDL_HINT_RENDER_BATCHING to "1", and you plan to make
+ *  OpenGL/D3D/whatever calls in addition to SDL render API calls. If all of
+ *  this applies, you should call SDL_RenderFlush() between calls to SDL's
+ *  render API and the low-level API you're using in cooperation.
+ *
+ *  In all other cases, you can ignore this function. This is only here to
+ *  get maximum performance out of a specific situation. In all other cases,
+ *  SDL will do the right thing, perhaps at a performance loss.
+ *
+ *  This function is first available in SDL 2.0.10, and is not needed in
+ *  2.0.9 and earlier, as earlier versions did not queue rendering commands
+ *  at all, instead flushing them to the OS immediately.
+ */
+extern DECLSPEC int SDLCALL SDL_RenderFlush(SDL_Renderer * renderer);
+
 
 /**
  *  \brief Bind the texture to the current OpenGL/ES/ES2 context for use with
@@ -898,6 +1065,27 @@ extern DECLSPEC int SDLCALL SDL_GL_BindTexture(SDL_Texture *texture, float *texw
  */
 extern DECLSPEC int SDLCALL SDL_GL_UnbindTexture(SDL_Texture *texture);
 
+/**
+ *  \brief Get the CAMetalLayer associated with the given Metal renderer
+ *
+ *  \param renderer The renderer to query
+ *
+ *  \return CAMetalLayer* on success, or NULL if the renderer isn't a Metal renderer
+ *
+ *  \sa SDL_RenderGetMetalCommandEncoder()
+ */
+extern DECLSPEC void *SDLCALL SDL_RenderGetMetalLayer(SDL_Renderer * renderer);
+
+/**
+ *  \brief Get the Metal command encoder for the current frame
+ *
+ *  \param renderer The renderer to query
+ *
+ *  \return id<MTLRenderCommandEncoder> on success, or NULL if the renderer isn't a Metal renderer
+ *
+ *  \sa SDL_RenderGetMetalLayer()
+ */
+extern DECLSPEC void *SDLCALL SDL_RenderGetMetalCommandEncoder(SDL_Renderer * renderer);
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus

+ 50 - 13
Source/ThirdParty/SDL/include/SDL_rwops.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -176,19 +176,48 @@ extern DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops * area);
 #define RW_SEEK_END 2       /**< Seek relative to the end of data */
 
 /**
- *  \name Read/write macros
+ *  Return the size of the file in this rwops, or -1 if unknown
+ */
+extern DECLSPEC Sint64 SDLCALL SDL_RWsize(SDL_RWops *context);
+
+/**
+ *  Seek to \c offset relative to \c whence, one of stdio's whence values:
+ *  RW_SEEK_SET, RW_SEEK_CUR, RW_SEEK_END
  *
- *  Macros to easily read and write from an SDL_RWops structure.
+ *  \return the final offset in the data stream, or -1 on error.
  */
-/* @{ */
-#define SDL_RWsize(ctx)         (ctx)->size(ctx)
-#define SDL_RWseek(ctx, offset, whence) (ctx)->seek(ctx, offset, whence)
-#define SDL_RWtell(ctx)         (ctx)->seek(ctx, 0, RW_SEEK_CUR)
-#define SDL_RWread(ctx, ptr, size, n)   (ctx)->read(ctx, ptr, size, n)
-#define SDL_RWwrite(ctx, ptr, size, n)  (ctx)->write(ctx, ptr, size, n)
-#define SDL_RWclose(ctx)        (ctx)->close(ctx)
-/* @} *//* Read/write macros */
+extern DECLSPEC Sint64 SDLCALL SDL_RWseek(SDL_RWops *context,
+                                          Sint64 offset, int whence);
+
+/**
+ *  Return the current offset in the data stream, or -1 on error.
+ */
+extern DECLSPEC Sint64 SDLCALL SDL_RWtell(SDL_RWops *context);
 
+/**
+ *  Read up to \c maxnum objects each of size \c size from the data
+ *  stream to the area pointed at by \c ptr.
+ *
+ *  \return the number of objects read, or 0 at error or end of file.
+ */
+extern DECLSPEC size_t SDLCALL SDL_RWread(SDL_RWops *context,
+                                          void *ptr, size_t size, size_t maxnum);
+
+/**
+ *  Write exactly \c num objects each of size \c size from the area
+ *  pointed at by \c ptr to data stream.
+ *
+ *  \return the number of objects written, or 0 at error or end of file.
+ */
+extern DECLSPEC size_t SDLCALL SDL_RWwrite(SDL_RWops *context,
+                                           const void *ptr, size_t size, size_t num);
+
+/**
+ *  Close and free an allocated SDL_RWops structure.
+ *
+ *  \return 0 if successful or -1 on write error when flushing data.
+ */
+extern DECLSPEC int SDLCALL SDL_RWclose(SDL_RWops *context);
 
 /**
  *  Load all the data from an SDL data stream.
@@ -209,9 +238,17 @@ extern DECLSPEC void *SDLCALL SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize,
 /**
  *  Load an entire file.
  *
- *  Convenience macro.
+ *  The data is allocated with a zero byte at the end (null terminated)
+ *
+ *  If \c datasize is not NULL, it is filled with the size of the data read.
+ *
+ *  If \c freesrc is non-zero, the stream will be closed after being read.
+ *
+ *  The data should be freed with SDL_free().
+ *
+ *  \return the data, or NULL if there was an error.
  */
-#define SDL_LoadFile(file, datasize)   SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, 1)
+extern DECLSPEC void *SDLCALL SDL_LoadFile(const char *file, size_t *datasize);
 
 /**
  *  \name Read endian functions

+ 2 - 2
Source/ThirdParty/SDL/include/SDL_scancode.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -38,7 +38,7 @@
  *  SDL_Event structure.
  *
  *  The values in this enumeration are based on the USB usage page standard:
- *  http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
+ *  https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
  */
 typedef enum
 {

+ 251 - 0
Source/ThirdParty/SDL/include/SDL_sensor.h

@@ -0,0 +1,251 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
+
+  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.
+*/
+
+/**
+ *  \file SDL_sensor.h
+ *
+ *  Include file for SDL sensor event handling
+ *
+ */
+
+#ifndef SDL_sensor_h_
+#define SDL_sensor_h_
+
+#include "SDL_stdinc.h"
+#include "SDL_error.h"
+
+#include "begin_code.h"
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+/**
+ *  \brief SDL_sensor.h
+ *
+ *  In order to use these functions, SDL_Init() must have been called
+ *  with the ::SDL_INIT_SENSOR flag.  This causes SDL to scan the system
+ *  for sensors, and load appropriate drivers.
+ */
+
+struct _SDL_Sensor;
+typedef struct _SDL_Sensor SDL_Sensor;
+
+/**
+ * This is a unique ID for a sensor for the time it is connected to the system,
+ * and is never reused for the lifetime of the application.
+ *
+ * The ID value starts at 0 and increments from there. The value -1 is an invalid ID.
+ */
+typedef Sint32 SDL_SensorID;
+
+/* The different sensors defined by SDL
+ *
+ * Additional sensors may be available, using platform dependent semantics.
+ *
+ * Hare are the additional Android sensors:
+ * https://developer.android.com/reference/android/hardware/SensorEvent.html#values
+ */
+typedef enum
+{
+    SDL_SENSOR_INVALID = -1,    /**< Returned for an invalid sensor */
+    SDL_SENSOR_UNKNOWN,         /**< Unknown sensor type */
+    SDL_SENSOR_ACCEL,           /**< Accelerometer */
+    SDL_SENSOR_GYRO             /**< Gyroscope */
+} SDL_SensorType;
+
+/**
+ * Accelerometer sensor
+ *
+ * The accelerometer returns the current acceleration in SI meters per
+ * second squared. This includes gravity, so a device at rest will have
+ * an acceleration of SDL_STANDARD_GRAVITY straight down.
+ *
+ * values[0]: Acceleration on the x axis
+ * values[1]: Acceleration on the y axis
+ * values[2]: Acceleration on the z axis
+ *
+ * For phones held in portrait mode, the axes are defined as follows:
+ * -X ... +X : left ... right
+ * -Y ... +Y : bottom ... top
+ * -Z ... +Z : farther ... closer
+ * 
+ * The axis data is not changed when the phone is rotated.
+ *
+ * \sa SDL_GetDisplayOrientation()
+ */
+#define SDL_STANDARD_GRAVITY    9.80665f
+
+/**
+ * Gyroscope sensor
+ *
+ * The gyroscope returns the current rate of rotation in radians per second.
+ * The rotation is positive in the counter-clockwise direction. That is,
+ * an observer looking from a positive location on one of the axes would
+ * see positive rotation on that axis when it appeared to be rotating
+ * counter-clockwise.
+ *
+ * values[0]: Angular speed around the x axis
+ * values[1]: Angular speed around the y axis
+ * values[2]: Angular speed around the z axis
+ *
+ * For phones held in portrait mode, the axes are defined as follows:
+ * -X ... +X : left ... right
+ * -Y ... +Y : bottom ... top
+ * -Z ... +Z : farther ... closer
+ * 
+ * The axis data is not changed when the phone is rotated.
+ *
+ * \sa SDL_GetDisplayOrientation()
+ */
+
+/* Function prototypes */
+
+/**
+ *  \brief Count the number of sensors attached to the system right now
+ */
+extern DECLSPEC int SDLCALL SDL_NumSensors(void);
+
+/**
+ *  \brief Get the implementation dependent name of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ * 
+ *  \return The sensor name, or NULL if device_index is out of range.
+ */
+extern DECLSPEC const char *SDLCALL SDL_SensorGetDeviceName(int device_index);
+
+/**
+ *  \brief Get the type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor type, or SDL_SENSOR_INVALID if device_index is out of range.
+ */
+extern DECLSPEC SDL_SensorType SDLCALL SDL_SensorGetDeviceType(int device_index);
+
+/**
+ *  \brief Get the platform dependent type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor platform dependent type, or -1 if device_index is out of range.
+ */
+extern DECLSPEC int SDLCALL SDL_SensorGetDeviceNonPortableType(int device_index);
+
+/**
+ *  \brief Get the instance ID of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor instance ID, or -1 if device_index is out of range.
+ */
+extern DECLSPEC SDL_SensorID SDLCALL SDL_SensorGetDeviceInstanceID(int device_index);
+
+/**
+ *  \brief Open a sensor for use.
+ *
+ *  The index passed as an argument refers to the N'th sensor on the system.
+ *
+ *  \return A sensor identifier, or NULL if an error occurred.
+ */
+extern DECLSPEC SDL_Sensor *SDLCALL SDL_SensorOpen(int device_index);
+
+/**
+ * Return the SDL_Sensor associated with an instance id.
+ */
+extern DECLSPEC SDL_Sensor *SDLCALL SDL_SensorFromInstanceID(SDL_SensorID instance_id);
+
+/**
+ *  \brief Get the implementation dependent name of a sensor.
+ *
+ *  \return The sensor name, or NULL if the sensor is NULL.
+ */
+extern DECLSPEC const char *SDLCALL SDL_SensorGetName(SDL_Sensor *sensor);
+
+/**
+ *  \brief Get the type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor type, or SDL_SENSOR_INVALID if the sensor is NULL.
+ */
+extern DECLSPEC SDL_SensorType SDLCALL SDL_SensorGetType(SDL_Sensor *sensor);
+
+/**
+ *  \brief Get the platform dependent type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor platform dependent type, or -1 if the sensor is NULL.
+ */
+extern DECLSPEC int SDLCALL SDL_SensorGetNonPortableType(SDL_Sensor *sensor);
+
+/**
+ *  \brief Get the instance ID of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor instance ID, or -1 if the sensor is NULL.
+ */
+extern DECLSPEC SDL_SensorID SDLCALL SDL_SensorGetInstanceID(SDL_Sensor *sensor);
+
+/**
+ *  Get the current state of an opened sensor.
+ *
+ *  The number of values and interpretation of the data is sensor dependent.
+ *
+ *  \param sensor The sensor to query
+ *  \param data A pointer filled with the current sensor state
+ *  \param num_values The number of values to write to data
+ *
+ *  \return 0 or -1 if an error occurred.
+ */
+extern DECLSPEC int SDLCALL SDL_SensorGetData(SDL_Sensor * sensor, float *data, int num_values);
+
+/**
+ *  Close a sensor previously opened with SDL_SensorOpen()
+ */
+extern DECLSPEC void SDLCALL SDL_SensorClose(SDL_Sensor * sensor);
+
+/**
+ *  Update the current state of the open sensors.
+ *
+ *  This is called automatically by the event loop if sensor events are enabled.
+ *
+ *  This needs to be called from the thread that initialized the sensor subsystem.
+ */
+extern DECLSPEC void SDLCALL SDL_SensorUpdate(void);
+
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+#include "close_code.h"
+
+#endif /* SDL_sensor_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_shape.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 41 - 24
Source/ThirdParty/SDL/include/SDL_stdinc.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -86,6 +86,28 @@
 #ifdef HAVE_FLOAT_H
 # include <float.h>
 #endif
+#if defined(HAVE_ALLOCA) && !defined(alloca)
+# if defined(HAVE_ALLOCA_H)
+#  include <alloca.h>
+# elif defined(__GNUC__)
+#  define alloca __builtin_alloca
+# elif defined(_MSC_VER)
+#  include <malloc.h>
+#  define alloca _alloca
+# elif defined(__WATCOMC__)
+#  include <malloc.h>
+# elif defined(__BORLANDC__)
+#  include <malloc.h>
+# elif defined(__DMC__)
+#  include <stdlib.h>
+# elif defined(__AIX__)
+#pragma alloca
+# elif defined(__MRC__)
+void *alloca(unsigned);
+# else
+char *alloca();
+# endif
+#endif
 
 /**
  *  The number of elements in an array.
@@ -328,28 +350,6 @@ SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int));
 extern "C" {
 #endif
 
-#if defined(HAVE_ALLOCA) && !defined(alloca)
-# if defined(HAVE_ALLOCA_H)
-#  include <alloca.h>
-# elif defined(__GNUC__)
-#  define alloca __builtin_alloca
-# elif defined(_MSC_VER)
-#  include <malloc.h>
-#  define alloca _alloca
-# elif defined(__WATCOMC__)
-#  include <malloc.h>
-# elif defined(__BORLANDC__)
-#  include <malloc.h>
-# elif defined(__DMC__)
-#  include <stdlib.h>
-# elif defined(__AIX__)
-#pragma alloca
-# elif defined(__MRC__)
-void *alloca(unsigned);
-# else
-char *alloca();
-# endif
-#endif
 #ifdef HAVE_ALLOCA
 #define SDL_stack_alloc(type, count)    (type*)alloca(sizeof(type)*(count))
 #define SDL_stack_free(data)
@@ -445,12 +445,12 @@ SDL_FORCE_INLINE void SDL_memset4(void *dst, Uint32 val, size_t dwords)
 #endif
 }
 
-
 extern DECLSPEC void *SDLCALL SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
 
 extern DECLSPEC void *SDLCALL SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
 extern DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len);
 
+extern DECLSPEC wchar_t *SDLCALL SDL_wcsdup(const wchar_t *wstr);
 extern DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t *wstr);
 extern DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
 extern DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
@@ -501,18 +501,35 @@ extern DECLSPEC int SDLCALL SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size
 #endif
 
 extern DECLSPEC double SDLCALL SDL_acos(double x);
+extern DECLSPEC float SDLCALL SDL_acosf(float x);
 extern DECLSPEC double SDLCALL SDL_asin(double x);
+extern DECLSPEC float SDLCALL SDL_asinf(float x);
 extern DECLSPEC double SDLCALL SDL_atan(double x);
+extern DECLSPEC float SDLCALL SDL_atanf(float x);
 extern DECLSPEC double SDLCALL SDL_atan2(double x, double y);
+extern DECLSPEC float SDLCALL SDL_atan2f(float x, float y);
 extern DECLSPEC double SDLCALL SDL_ceil(double x);
+extern DECLSPEC float SDLCALL SDL_ceilf(float x);
 extern DECLSPEC double SDLCALL SDL_copysign(double x, double y);
+extern DECLSPEC float SDLCALL SDL_copysignf(float x, float y);
 extern DECLSPEC double SDLCALL SDL_cos(double x);
 extern DECLSPEC float SDLCALL SDL_cosf(float x);
+extern DECLSPEC double SDLCALL SDL_exp(double x);
+extern DECLSPEC float SDLCALL SDL_expf(float x);
 extern DECLSPEC double SDLCALL SDL_fabs(double x);
+extern DECLSPEC float SDLCALL SDL_fabsf(float x);
 extern DECLSPEC double SDLCALL SDL_floor(double x);
+extern DECLSPEC float SDLCALL SDL_floorf(float x);
+extern DECLSPEC double SDLCALL SDL_fmod(double x, double y);
+extern DECLSPEC float SDLCALL SDL_fmodf(float x, float y);
 extern DECLSPEC double SDLCALL SDL_log(double x);
+extern DECLSPEC float SDLCALL SDL_logf(float x);
+extern DECLSPEC double SDLCALL SDL_log10(double x);
+extern DECLSPEC float SDLCALL SDL_log10f(float x);
 extern DECLSPEC double SDLCALL SDL_pow(double x, double y);
+extern DECLSPEC float SDLCALL SDL_powf(float x, float y);
 extern DECLSPEC double SDLCALL SDL_scalbn(double x, int n);
+extern DECLSPEC float SDLCALL SDL_scalbnf(float x, int n);
 extern DECLSPEC double SDLCALL SDL_sin(double x);
 extern DECLSPEC float SDLCALL SDL_sinf(float x);
 extern DECLSPEC double SDLCALL SDL_sqrt(double x);

+ 42 - 1
Source/ThirdParty/SDL/include/SDL_surface.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -19,6 +19,9 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+
+// Modified by Yao Wei Tjong for Urho3D
+
 /**
  *  \file SDL_surface.h
  *
@@ -53,6 +56,12 @@ extern "C" {
 #define SDL_PREALLOC        0x00000001  /**< Surface uses preallocated memory */
 #define SDL_RLEACCEL        0x00000002  /**< Surface is RLE encoded */
 #define SDL_DONTFREE        0x00000004  /**< Surface is referenced internally */
+// Urho3D - bug fix - check if SIMD is supported
+#ifdef __EMSCRIPTEN__
+#define SDL_SIMD_ALIGNED    0
+#else
+#define SDL_SIMD_ALIGNED    0x00000008  /**< Surface uses aligned memory */
+#endif
 /* @} *//* Surface flags */
 
 /**
@@ -97,6 +106,17 @@ typedef struct SDL_Surface
 typedef int (SDLCALL *SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect,
                                  struct SDL_Surface * dst, SDL_Rect * dstrect);
 
+/**
+ * \brief The formula used for converting between YUV and RGB
+ */
+typedef enum
+{
+    SDL_YUV_CONVERSION_JPEG,        /**< Full range JPEG */
+    SDL_YUV_CONVERSION_BT601,       /**< BT.601 (the default) */
+    SDL_YUV_CONVERSION_BT709,       /**< BT.709 */
+    SDL_YUV_CONVERSION_AUTOMATIC    /**< BT.601 for SD content, BT.709 for HD content */
+} SDL_YUV_CONVERSION_MODE;
+
 /**
  *  Allocate and free an RGB surface.
  *
@@ -237,6 +257,13 @@ extern DECLSPEC int SDLCALL SDL_SetSurfaceRLE(SDL_Surface * surface,
 extern DECLSPEC int SDLCALL SDL_SetColorKey(SDL_Surface * surface,
                                             int flag, Uint32 key);
 
+/**
+ *  \brief Returns whether the surface has a color key
+ *
+ *  \return SDL_TRUE if the surface has a color key, or SDL_FALSE if the surface is NULL or has no color key
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasColorKey(SDL_Surface * surface);
+
 /**
  *  \brief Gets the color key (transparent pixel) in a blittable surface.
  *
@@ -509,6 +536,20 @@ extern DECLSPEC int SDLCALL SDL_LowerBlitScaled
     (SDL_Surface * src, SDL_Rect * srcrect,
     SDL_Surface * dst, SDL_Rect * dstrect);
 
+/**
+ *  \brief Set the YUV conversion mode
+ */
+extern DECLSPEC void SDLCALL SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_MODE mode);
+
+/**
+ *  \brief Get the YUV conversion mode
+ */
+extern DECLSPEC SDL_YUV_CONVERSION_MODE SDLCALL SDL_GetYUVConversionMode(void);
+
+/**
+ *  \brief Get the YUV conversion mode, returning the correct mode for the resolution when the current conversion mode is SDL_YUV_CONVERSION_AUTOMATIC
+ */
+extern DECLSPEC SDL_YUV_CONVERSION_MODE SDLCALL SDL_GetYUVConversionModeForResolution(int width, int height);
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus

+ 64 - 1
Source/ThirdParty/SDL/include/SDL_system.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -76,6 +76,18 @@ extern DECLSPEC SDL_bool SDLCALL SDL_DXGIGetOutputInfo( int displayIndex, int *a
 #endif /* __WIN32__ */
 
 
+/* Platform specific functions for Linux */
+#ifdef __LINUX__
+
+/**
+   \brief Sets the UNIX nice value for a thread, using setpriority() if possible, and RealtimeKit if available.
+
+   \return 0 on success, or -1 on error.
+ */
+extern DECLSPEC int SDLCALL SDL_LinuxSetThreadPriority(Sint64 threadID, int priority);
+ 
+#endif /* __LINUX__ */
+	
 /* Platform specific functions for iOS */
 #if defined(__IPHONEOS__) && __IPHONEOS__
 
@@ -108,6 +120,26 @@ extern DECLSPEC void * SDLCALL SDL_AndroidGetJNIEnv(void);
  */
 extern DECLSPEC void * SDLCALL SDL_AndroidGetActivity(void);
 
+/**
+   \brief Return true if the application is running on Android TV
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsAndroidTV(void);
+
+/**
+   \brief Return true if the application is running on a Chromebook
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsChromebook(void);
+
+/**
+  \brief Return true is the application is running on a Samsung DeX docking station
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsDeXMode(void);
+
+/**
+ \brief Trigger the Android system back button behavior.
+ */
+extern DECLSPEC void SDLCALL SDL_AndroidBackButton(void);
+
 /**
    See the official Android developer guide for more information:
    http://developer.android.com/guide/topics/data/data-storage.html
@@ -169,6 +201,25 @@ typedef enum
 } SDL_WinRT_Path;
 
 
+/**
+ *  \brief WinRT Device Family
+ */
+typedef enum
+{
+    /** \brief Unknown family  */
+    SDL_WINRT_DEVICEFAMILY_UNKNOWN,
+
+    /** \brief Desktop family*/
+    SDL_WINRT_DEVICEFAMILY_DESKTOP,
+
+    /** \brief Mobile family (for example smartphone) */
+    SDL_WINRT_DEVICEFAMILY_MOBILE,
+
+    /** \brief XBox family */
+    SDL_WINRT_DEVICEFAMILY_XBOX,
+} SDL_WinRT_DeviceFamily;
+
+
 /**
  *  \brief Retrieves a WinRT defined path on the local file system
  *
@@ -203,8 +254,20 @@ extern DECLSPEC const wchar_t * SDLCALL SDL_WinRTGetFSPathUNICODE(SDL_WinRT_Path
  */
 extern DECLSPEC const char * SDLCALL SDL_WinRTGetFSPathUTF8(SDL_WinRT_Path pathType);
 
+/**
+ *  \brief Detects the device family of WinRT plattform on runtime
+ *
+ *  \return Device family
+ */
+extern DECLSPEC SDL_WinRT_DeviceFamily SDLCALL SDL_WinRTGetDeviceFamily();
+
 #endif /* __WINRT__ */
 
+/**
+ \brief Return true if the current device is a tablet.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsTablet(void);
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }

+ 17 - 14
Source/ThirdParty/SDL/include/SDL_syswm.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -33,23 +33,17 @@
 #include "SDL_video.h"
 #include "SDL_version.h"
 
-#include "begin_code.h"
-/* Set up for C function definitions, even when using C++ */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
- *  \file SDL_syswm.h
+ *  \brief SDL_syswm.h
  *
  *  Your application has access to a special type of event ::SDL_SYSWMEVENT,
  *  which contains window-manager specific information and arrives whenever
  *  an unhandled window event occurs.  This event is ignored by default, but
  *  you can enable it with SDL_EventState().
  */
-#ifdef SDL_PROTOTYPES_ONLY
 struct SDL_SysWMinfo;
-#else
+
+#if !defined(SDL_PROTOTYPES_ONLY)
 
 #if defined(SDL_VIDEO_DRIVER_WINDOWS)
 #ifndef WIN32_LEAN_AND_MEAN
@@ -109,7 +103,16 @@ typedef void *EGLSurface;
 #if defined(SDL_VIDEO_DRIVER_VIVANTE)
 #include "SDL_egl.h"
 #endif
+#endif /* SDL_PROTOTYPES_ONLY */
+
+
+#include "begin_code.h"
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
 
+#if !defined(SDL_PROTOTYPES_ONLY)
 /**
  *  These are the various supported windowing subsystems
  */
@@ -122,7 +125,7 @@ typedef enum
     SDL_SYSWM_COCOA,
     SDL_SYSWM_UIKIT,
     SDL_SYSWM_WAYLAND,
-    SDL_SYSWM_MIR,
+    SDL_SYSWM_MIR,  /* no longer available, left for API/ABI compatibility. Remove in 2.1! */
     SDL_SYSWM_WINRT,
     SDL_SYSWM_ANDROID,
     SDL_SYSWM_VIVANTE,
@@ -257,11 +260,11 @@ struct SDL_SysWMinfo
             struct wl_shell_surface *shell_surface; /**< Wayland shell_surface (window manager handle) */
         } wl;
 #endif
-#if defined(SDL_VIDEO_DRIVER_MIR)
+#if defined(SDL_VIDEO_DRIVER_MIR)  /* no longer available, left for API/ABI compatibility. Remove in 2.1! */
         struct
         {
-            struct MirConnection *connection;  /**< Mir display server connection */
-            struct MirSurface *surface;  /**< Mir surface */
+            void *connection;  /**< Mir display server connection */
+            void *surface;  /**< Mir surface */
         } mir;
 #endif
 

+ 39 - 4
Source/ThirdParty/SDL/include/SDL_thread.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -54,12 +54,13 @@ typedef unsigned int SDL_TLSID;
 /**
  *  The SDL thread priority.
  *
- *  \note On many systems you require special privileges to set high priority.
+ *  \note On many systems you require special privileges to set high or time critical priority.
  */
 typedef enum {
     SDL_THREAD_PRIORITY_LOW,
     SDL_THREAD_PRIORITY_NORMAL,
-    SDL_THREAD_PRIORITY_HIGH
+    SDL_THREAD_PRIORITY_HIGH,
+    SDL_THREAD_PRIORITY_TIME_CRITICAL
 } SDL_ThreadPriority;
 
 /**
@@ -105,14 +106,24 @@ SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,
                  pfnSDL_CurrentBeginThread pfnBeginThread,
                  pfnSDL_CurrentEndThread pfnEndThread);
 
+extern DECLSPEC SDL_Thread *SDLCALL
+SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
+                 const char *name, const size_t stacksize, void *data,
+                 pfnSDL_CurrentBeginThread pfnBeginThread,
+                 pfnSDL_CurrentEndThread pfnEndThread);
+
+
 /**
  *  Create a thread.
  */
 #if defined(SDL_CreateThread) && SDL_DYNAMIC_API
 #undef SDL_CreateThread
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
+#undef SDL_CreateThreadWithStackSize
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize_REAL(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
 #else
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
 #endif
 
 #elif defined(__OS2__)
@@ -132,15 +143,31 @@ extern DECLSPEC SDL_Thread *SDLCALL
 SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,
                  pfnSDL_CurrentBeginThread pfnBeginThread,
                  pfnSDL_CurrentEndThread pfnEndThread);
+extern DECLSPEC SDL_Thread *SDLCALL
+SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, const size_t stacksize, void *data,
+                 pfnSDL_CurrentBeginThread pfnBeginThread,
+                 pfnSDL_CurrentEndThread pfnEndThread);
 #if defined(SDL_CreateThread) && SDL_DYNAMIC_API
 #undef SDL_CreateThread
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
+#undef SDL_CreateThreadWithStackSize
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
 #else
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
 #endif
 
 #else
 
+/**
+ *  Create a thread with a default stack size.
+ *
+ *  This is equivalent to calling:
+ *  SDL_CreateThreadWithStackSize(fn, name, 0, data);
+ */
+extern DECLSPEC SDL_Thread *SDLCALL
+SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data);
+
 /**
  *  Create a thread.
  *
@@ -158,9 +185,17 @@ SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,
  *   If a system imposes requirements, SDL will try to munge the string for
  *    it (truncate, etc), but the original string contents will be available
  *    from SDL_GetThreadName().
+ *
+ *   The size (in bytes) of the new stack can be specified. Zero means "use
+ *    the system default" which might be wildly different between platforms
+ *    (x86 Linux generally defaults to eight megabytes, an embedded device
+ *    might be a few kilobytes instead).
+ *
+ *   In SDL 2.1, stacksize will be folded into the original SDL_CreateThread
+ *    function.
  */
 extern DECLSPEC SDL_Thread *SDLCALL
-SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data);
+SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, const size_t stacksize, void *data);
 
 #endif
 

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_timer.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 17 - 1
Source/ThirdParty/SDL/include/SDL_touch.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -41,6 +41,14 @@ extern "C" {
 typedef Sint64 SDL_TouchID;
 typedef Sint64 SDL_FingerID;
 
+typedef enum
+{
+    SDL_TOUCH_DEVICE_INVALID = -1,
+    SDL_TOUCH_DEVICE_DIRECT,            /* touch screen with window-relative coordinates */
+    SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE, /* trackpad with absolute device coordinates */
+    SDL_TOUCH_DEVICE_INDIRECT_RELATIVE  /* trackpad with screen cursor-relative coordinates */
+} SDL_TouchDeviceType;
+
 typedef struct SDL_Finger
 {
     SDL_FingerID id;
@@ -52,6 +60,9 @@ typedef struct SDL_Finger
 /* Used as the device ID for mouse events simulated with touch input */
 #define SDL_TOUCH_MOUSEID ((Uint32)-1)
 
+/* Used as the SDL_TouchID for touch events simulated with mouse input */
+#define SDL_MOUSE_TOUCHID ((Sint64)-1)
+
 
 /* Function prototypes */
 
@@ -65,6 +76,11 @@ extern DECLSPEC int SDLCALL SDL_GetNumTouchDevices(void);
  */
 extern DECLSPEC SDL_TouchID SDLCALL SDL_GetTouchDevice(int index);
 
+/**
+ * \brief Get the type of the given touch device.
+ */
+extern DECLSPEC SDL_TouchDeviceType SDLCALL SDL_GetTouchDeviceType(SDL_TouchID touchID);
+
 /**
  *  \brief Get the number of active fingers for a given touch device.
  */

+ 1 - 1
Source/ThirdParty/SDL/include/SDL_types.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 2 - 2
Source/ThirdParty/SDL/include/SDL_version.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -59,7 +59,7 @@ typedef struct SDL_version
 */
 #define SDL_MAJOR_VERSION   2
 #define SDL_MINOR_VERSION   0
-#define SDL_PATCHLEVEL      7
+#define SDL_PATCHLEVEL      10
 
 /**
  *  \brief Macro to determine SDL version program was compiled against.

+ 44 - 15
Source/ThirdParty/SDL/include/SDL_video.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -111,7 +111,9 @@ typedef enum
     SDL_WINDOW_MOUSE_FOCUS = 0x00000400,        /**< window has mouse focus */
     SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
     SDL_WINDOW_FOREIGN = 0x00000800,            /**< window not created by SDL */
-    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported */
+    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported.
+                                                     On macOS NSHighResolutionCapable must be set true in the
+                                                     application's Info.plist for this to have any effect. */
     SDL_WINDOW_MOUSE_CAPTURE = 0x00004000,      /**< window has mouse captured (unrelated to INPUT_GRABBED) */
     SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000,      /**< window should always be above others */
     SDL_WINDOW_SKIP_TASKBAR  = 0x00010000,      /**< window should not be added to the taskbar */
@@ -168,6 +170,24 @@ typedef enum
     SDL_WINDOWEVENT_HIT_TEST        /**< Window had a hit test that wasn't SDL_HITTEST_NORMAL. */
 } SDL_WindowEventID;
 
+/**
+ *  \brief Event subtype for display events
+ */
+typedef enum
+{
+    SDL_DISPLAYEVENT_NONE,          /**< Never used */
+    SDL_DISPLAYEVENT_ORIENTATION    /**< Display orientation has changed to data1 */
+} SDL_DisplayEventID;
+
+typedef enum
+{
+    SDL_ORIENTATION_UNKNOWN,            /**< The display orientation can't be determined */
+    SDL_ORIENTATION_LANDSCAPE,          /**< The display is in landscape mode, with the right side up, relative to portrait mode */
+    SDL_ORIENTATION_LANDSCAPE_FLIPPED,  /**< The display is in landscape mode, with the left side up, relative to portrait mode */
+    SDL_ORIENTATION_PORTRAIT,           /**< The display is in portrait mode */
+    SDL_ORIENTATION_PORTRAIT_FLIPPED    /**< The display is in portrait mode, upside down */
+} SDL_DisplayOrientation;
+
 /**
  *  \brief An opaque handle to an OpenGL context.
  */
@@ -315,18 +335,6 @@ extern DECLSPEC const char * SDLCALL SDL_GetDisplayName(int displayIndex);
  */
 extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect);
 
-/**
- *  \brief Get the dots/pixels-per-inch for a display
- *
- *  \note Diagonal, horizontal and vertical DPI can all be optionally
- *        returned if the parameter is non-NULL.
- *
- *  \return 0 on success, or -1 if no DPI information is available or the index is out of range.
- *
- *  \sa SDL_GetNumVideoDisplays()
- */
-extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi);
-
 /**
  *  \brief Get the usable desktop area represented by a display, with the
  *         primary display located at 0,0
@@ -346,6 +354,27 @@ extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, fl
  */
 extern DECLSPEC int SDLCALL SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect);
 
+/**
+ *  \brief Get the dots/pixels-per-inch for a display
+ *
+ *  \note Diagonal, horizontal and vertical DPI can all be optionally
+ *        returned if the parameter is non-NULL.
+ *
+ *  \return 0 on success, or -1 if no DPI information is available or the index is out of range.
+ *
+ *  \sa SDL_GetNumVideoDisplays()
+ */
+extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi);
+
+/**
+ *  \brief Get the orientation of a display
+ *
+ *  \return The orientation of the display, or SDL_ORIENTATION_UNKNOWN if it isn't available.
+ *
+ *  \sa SDL_GetNumVideoDisplays()
+ */
+extern DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetDisplayOrientation(int displayIndex);
+
 /**
  *  \brief Returns the number of available display modes.
  *
@@ -471,7 +500,7 @@ extern DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window * window);
  *  If the window is created with any of the SDL_WINDOW_OPENGL or
  *  SDL_WINDOW_VULKAN flags, then the corresponding LoadLibrary function
  *  (SDL_GL_LoadLibrary or SDL_Vulkan_LoadLibrary) is called and the
- *  corrensponding UnloadLibrary function is called by SDL_DestroyWindow().
+ *  corresponding UnloadLibrary function is called by SDL_DestroyWindow().
  *
  *  If SDL_WINDOW_VULKAN is specified and there isn't a working Vulkan driver,
  *  SDL_CreateWindow() will fail because SDL_Vulkan_LoadLibrary() will fail.

+ 41 - 20
Source/ThirdParty/SDL/include/SDL_vulkan.h

@@ -69,30 +69,43 @@ typedef VkSurfaceKHR SDL_vulkanSurface; /* for compatibility with Tizen */
  *  \brief Dynamically load a Vulkan loader library.
  *
  *  \param [in] path The platform dependent Vulkan loader library name, or
- *              \c NULL to open the default Vulkan loader library.
+ *              \c NULL.
  *
  *  \return \c 0 on success, or \c -1 if the library couldn't be loaded.
  *
- *  This should be done after initializing the video driver, but before
+ *  If \a path is NULL SDL will use the value of the environment variable
+ *  \c SDL_VULKAN_LIBRARY, if set, otherwise it loads the default Vulkan
+ *  loader library.
+ *
+ *  This should be called after initializing the video driver, but before
  *  creating any Vulkan windows. If no Vulkan loader library is loaded, the
  *  default library will be loaded upon creation of the first Vulkan window.
  *
- *  \note If you specify a non-NULL \a path, you should retrieve all of the
- *        Vulkan functions used in your program from the dynamic library using
+ *  \note It is fairly common for Vulkan applications to link with \a libvulkan
+ *        instead of explicitly loading it at run time. This will work with
+ *        SDL provided the application links to a dynamic library and both it
+ *        and SDL use the same search path.
+ *
+ *  \note If you specify a non-NULL \c path, an application should retrieve all
+ *        of the Vulkan functions it uses from the dynamic library using
  *        \c SDL_Vulkan_GetVkGetInstanceProcAddr() unless you can guarantee
- *        \a path points to the same vulkan loader library that you linked to.
+ *        \c path points to the same vulkan loader library the application
+ *        linked to.
  *
  *  \note On Apple devices, if \a path is NULL, SDL will attempt to find
  *        the vkGetInstanceProcAddr address within all the mach-o images of
- *        the current process. This is because the currently (v0.17.0)
- *        recommended MoltenVK (Vulkan on Metal) usage is as a static library.
- *        If it is not found then SDL will attempt to load \c libMoltenVK.dylib.
- *        Applications using the dylib alternative therefore do not need to do
- *        anything special when calling SDL.
- *
- *  \note On non-Apple devices, SDL requires you to either not link to the
- *        Vulkan loader or link to a dynamic library version. This limitation
- *        may be removed in a future version of SDL.
+ *        the current process. This is because it is fairly common for Vulkan
+ *        applications to link with libvulkan (and historically MoltenVK was
+ *        provided as a static library). If it is not found then, on macOS, SDL
+ *        will attempt to load \c vulkan.framework/vulkan, \c libvulkan.1.dylib,
+ *        followed by \c libvulkan.dylib, in that order.
+ *        On iOS SDL will attempt to load \c libvulkan.dylib only. Applications
+ *        using a dynamic framework or .dylib must ensure it is included in its
+ *        application bundle.
+ *
+ *  \note On non-Apple devices, application linking with a static libvulkan is
+ *        not supported. Either do not link to the Vulkan loader or link to a
+ *        dynamic library version.
  *
  *  \note This function will fail if there are no working Vulkan drivers
  *        installed.
@@ -122,11 +135,11 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void);
  *  \brief Get the names of the Vulkan instance extensions needed to create
  *         a surface with \c SDL_Vulkan_CreateSurface().
  *
- *  \param [in]     window Window for which the required Vulkan instance
+ *  \param [in]     \c NULL or window Window for which the required Vulkan instance
  *                  extensions should be retrieved
- *  \param [in,out] count pointer to an \c unsigned related to the number of
+ *  \param [in,out] pCount pointer to an \c unsigned related to the number of
  *                  required Vulkan instance extensions
- *  \param [out]    names \c NULL or a pointer to an array to be filled with the
+ *  \param [out]    pNames \c NULL or a pointer to an array to be filled with the
  *                  required Vulkan instance extensions
  *
  *  \return \c SDL_TRUE on success, \c SDL_FALSE on error.
@@ -141,18 +154,23 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void);
  *  returned instead of \c SDL_TRUE, to indicate that not all the required
  *  extensions were returned.
  *
+ *  \note If \c window is not NULL, it will be checked against its creation
+ *        flags to ensure that the Vulkan flag is present. This parameter
+ *        will be removed in a future major release.
+ *
  *  \note The returned list of extensions will contain \c VK_KHR_surface
  *        and zero or more platform specific extensions
  *
  *  \note The extension names queried here must be enabled when calling
  *        VkCreateInstance, otherwise surface creation will fail.
  *
- *  \note \c window should have been created with the \c SDL_WINDOW_VULKAN flag.
+ *  \note \c window should have been created with the \c SDL_WINDOW_VULKAN flag
+ *        or be \c NULL
  *
  *  \code
  *  unsigned int count;
  *  // get count of required extensions
- *  if(!SDL_Vulkan_GetInstanceExtensions(window, &count, NULL))
+ *  if(!SDL_Vulkan_GetInstanceExtensions(NULL, &count, NULL))
  *      handle_error();
  *
  *  static const char *const additionalExtensions[] =
@@ -166,7 +184,7 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void);
  *      handle_error();
  *
  *  // get names of required extensions
- *  if(!SDL_Vulkan_GetInstanceExtensions(window, &count, names))
+ *  if(!SDL_Vulkan_GetInstanceExtensions(NULL, &count, names))
  *      handle_error();
  *
  *  // copy additional extensions after required extensions
@@ -240,6 +258,9 @@ extern DECLSPEC SDL_bool SDLCALL SDL_Vulkan_CreateSurface(
  * platform with high-DPI support (Apple calls this "Retina"), and not disabled
  * by the \c SDL_HINT_VIDEO_HIGHDPI_DISABLED hint.
  *
+ *  \note On macOS high-DPI support must be enabled for an application by
+ *        setting NSHighResolutionCapable to true in its Info.plist.
+ *
  *  \sa SDL_GetWindowSize()
  *  \sa SDL_CreateWindow()
  */

+ 4 - 1
Source/ThirdParty/SDL/include/begin_code.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -112,6 +112,9 @@
 #ifdef _MSC_VER
 #pragma warning(disable: 4103)
 #endif
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpragma-pack"
+#endif
 #ifdef __BORLANDC__
 #pragma nopackwarning
 #endif

+ 4 - 1
Source/ThirdParty/SDL/include/close_code.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -26,6 +26,9 @@
  *  after you finish any function and structure declarations in your headers
  */
 
+#ifndef _begin_code_h
+#error close_code.h included without matching begin_code.h
+#endif
 #undef _begin_code_h
 
 /* Reset structure packing at previous byte alignment */

+ 73 - 15
Source/ThirdParty/SDL/src/SDL.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -18,9 +18,6 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
-
-// Modified by Lasse Oorni for Urho3D
-
 #include "./SDL_internal.h"
 
 #if defined(__WIN32__)
@@ -36,6 +33,7 @@
 #include "events/SDL_events_c.h"
 #include "haptic/SDL_haptic_c.h"
 #include "joystick/SDL_joystick_c.h"
+#include "sensor/SDL_sensor_c.h"
 
 /* Initialization/Cleanup routines */
 #if !SDL_TIMERS_DISABLED
@@ -126,11 +124,11 @@ SDL_InitSubSystem(Uint32 flags)
     }
 
 #if SDL_VIDEO_DRIVER_WINDOWS
-	if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
-		if (SDL_HelperWindowCreate() < 0) {
-			return -1;
-		}
-	}
+    if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
+        if (SDL_HelperWindowCreate() < 0) {
+            return -1;
+        }
+    }
 #endif
 
 #if !SDL_TIMERS_DISABLED
@@ -141,10 +139,9 @@ SDL_InitSubSystem(Uint32 flags)
     if ((flags & SDL_INIT_EVENTS)) {
 #if !SDL_EVENTS_DISABLED
         if (SDL_PrivateShouldInitSubsystem(SDL_INIT_EVENTS)) {
-            if (SDL_StartEventLoop() < 0) {
+            if (SDL_EventsInit() < 0) {
                 return (-1);
             }
-            SDL_QuitInit();
         }
         SDL_PrivateSubsystemRefCountIncr(SDL_INIT_EVENTS);
 #else
@@ -235,6 +232,20 @@ SDL_InitSubSystem(Uint32 flags)
 #endif
     }
 
+    /* Initialize the sensor subsystem */
+    if ((flags & SDL_INIT_SENSOR)){
+#if !SDL_SENSOR_DISABLED
+        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_SENSOR)) {
+            if (SDL_SensorInit() < 0) {
+                return (-1);
+            }
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_SENSOR);
+#else
+        return SDL_SetError("SDL not built with sensor support");
+#endif
+    }
+
     return (0);
 }
 
@@ -248,6 +259,15 @@ void
 SDL_QuitSubSystem(Uint32 flags)
 {
     /* Shut down requested initialized subsystems */
+#if !SDL_SENSOR_DISABLED
+    if ((flags & SDL_INIT_SENSOR)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_SENSOR)) {
+            SDL_SensorQuit();
+        }
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_SENSOR);
+    }
+#endif
+
 #if !SDL_JOYSTICK_DISABLED
     if ((flags & SDL_INIT_GAMECONTROLLER)) {
         /* game controller implies joystick */
@@ -312,8 +332,7 @@ SDL_QuitSubSystem(Uint32 flags)
 #if !SDL_EVENTS_DISABLED
     if ((flags & SDL_INIT_EVENTS)) {
         if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_EVENTS)) {
-            SDL_QuitQuit();
-            SDL_StopEventLoop();
+            SDL_EventsQuit();
         }
         SDL_PrivateSubsystemRefCountDecr(SDL_INIT_EVENTS);
     }
@@ -327,6 +346,12 @@ SDL_WasInit(Uint32 flags)
     int num_subsystems = SDL_arraysize(SDL_SubsystemRefCount);
     Uint32 initialized = 0;
 
+    /* Fast path for checking one flag */
+    if (SDL_HasExactlyOneBitSet32(flags)) {
+        int subsystem_index = SDL_MostSignificantBitIndex32(flags);
+        return SDL_SubsystemRefCount[subsystem_index] ? flags : 0;
+    }
+
     if (!flags) {
         flags = SDL_INIT_EVERYTHING;
     }
@@ -454,7 +479,40 @@ SDL_GetPlatform()
 #endif
 }
 
-// Urho3D: removed offending _DllMainCRTStartup function which interfered with CRT initialization
-// when building as a shared library
+SDL_bool
+SDL_IsTablet()
+{
+#if __ANDROID__
+    extern SDL_bool SDL_IsAndroidTablet(void);
+    return SDL_IsAndroidTablet();
+#elif __IPHONEOS__
+    extern SDL_bool SDL_IsIPad(void);
+    return SDL_IsIPad();
+#else
+    return SDL_FALSE;
+#endif
+}
+
+#if defined(__WIN32__)
+
+#if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB)
+/* Need to include DllMain() on Watcom C for some reason.. */
+
+BOOL APIENTRY
+_DllMainCRTStartup(HANDLE hModule,
+                   DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+    switch (ul_reason_for_call) {
+    case DLL_PROCESS_ATTACH:
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+    case DLL_PROCESS_DETACH:
+        break;
+    }
+    return TRUE;
+}
+#endif /* Building DLL */
+
+#endif /* __WIN32__ */
 
 /* vi: set sts=4 ts=4 sw=4 expandtab: */

+ 23 - 6
Source/ThirdParty/SDL/src/SDL_assert.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -120,20 +120,36 @@ static void SDL_GenerateAssertionReport(void)
 }
 
 
+#if defined(__WATCOMC__)
+#pragma aux SDL_ExitProcess aborts;
+#endif
 static SDL_NORETURN void SDL_ExitProcess(int exitcode)
 {
 #ifdef __WIN32__
+    /* "if you do not know the state of all threads in your process, it is
+       better to call TerminateProcess than ExitProcess"
+       https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx */
+    TerminateProcess(GetCurrentProcess(), exitcode);
+    /* MingW doesn't have TerminateProcess marked as noreturn, so add an
+       ExitProcess here that will never be reached but make MingW happy. */
     ExitProcess(exitcode);
 #elif defined(__EMSCRIPTEN__)
     emscripten_cancel_main_loop();  /* this should "kill" the app. */
     emscripten_force_exit(exitcode);  /* this should "kill" the app. */
     exit(exitcode);
+#elif defined(__HAIKU__)  /* Haiku has _Exit, but it's not marked noreturn. */
+    _exit(exitcode);
+#elif defined(HAVE__EXIT) /* Upper case _Exit() */
+    _Exit(exitcode);
 #else
     _exit(exitcode);
 #endif
 }
 
 
+#if defined(__WATCOMC__)
+#pragma aux SDL_AbortAssertion aborts;
+#endif
 static SDL_NORETURN void SDL_AbortAssertion(void)
 {
     SDL_Quit();
@@ -168,6 +184,7 @@ SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
 
     (void) userdata;  /* unused in default handler. */
 
+    /* !!! FIXME: why is this using SDL_stack_alloc and not just "char message[SDL_MAX_LOG_MESSAGE];" ? */
     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
     if (!message) {
         /* Uh oh, we're in real trouble now... */
@@ -239,7 +256,7 @@ SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
             SDL_bool okay = SDL_TRUE;
             char *buf = (char *) EM_ASM_INT({
                 var str =
-                    Pointer_stringify($0) + '\n\n' +
+                    UTF8ToString($0) + '\n\n' +
                     'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
                 var reply = window.prompt(str, "i");
                 if (reply === null) {
@@ -359,10 +376,6 @@ SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
 
     switch (state)
     {
-        case SDL_ASSERTION_ABORT:
-            SDL_AbortAssertion();
-            return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
-
         case SDL_ASSERTION_ALWAYS_IGNORE:
             state = SDL_ASSERTION_IGNORE;
             data->always_ignore = 1;
@@ -372,6 +385,10 @@ SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
         case SDL_ASSERTION_RETRY:
         case SDL_ASSERTION_BREAK:
             break;  /* macro handles these. */
+
+        case SDL_ASSERTION_ABORT:
+            SDL_AbortAssertion();
+            /*break;  ...shouldn't return, but oh well. */
     }
 
     assertion_running--;

+ 6 - 1
Source/ThirdParty/SDL/src/SDL_assert_c.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -19,6 +19,11 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_assert_c_h_
+#define SDL_assert_c_h_
+
 extern void SDL_AssertionsQuit(void);
 
+#endif /* SDL_assert_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */

+ 1 - 1
Source/ThirdParty/SDL/src/SDL_dataqueue.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 1 - 1
Source/ThirdParty/SDL/src/SDL_dataqueue.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 27 - 1
Source/ThirdParty/SDL/src/SDL_error.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -76,6 +76,16 @@ SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
             case 0:            /* Malformed format string.. */
                 --fmt;
                 break;
+            case 'l':
+                switch (*fmt++) {
+                case 0:        /* Malformed format string.. */
+                    --fmt;
+                    break;
+                case 'i': case 'd': case 'u': case 'x': case 'X':
+                    error->args[error->argc++].value_l = va_arg(ap, long);
+                    break;
+                }
+                break;
             case 'c':
             case 'i':
             case 'd':
@@ -219,6 +229,22 @@ SDL_GetErrorMsg(char *errstr, int maxlen)
                        && spot < (tmp + SDL_arraysize(tmp) - 2)) {
                     *spot++ = *fmt++;
                 }
+                if (*fmt == 'l') {
+                    *spot++ = *fmt++;
+                    *spot++ = *fmt++;
+                    *spot++ = '\0';
+                    switch (spot[-2]) {
+                    case 'i': case 'd': case 'u': case 'x': case 'X':
+                      len = SDL_snprintf(msg, maxlen, tmp,
+                                         error->args[argi++].value_l);
+                      if (len > 0) {
+                          msg += len;
+                          maxlen -= len;
+                      }
+                      break;
+                    }
+                    continue;
+                }
                 *spot++ = *fmt++;
                 *spot++ = '\0';
                 switch (spot[-2]) {

+ 2 - 1
Source/ThirdParty/SDL/src/SDL_error_c.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -51,6 +51,7 @@ typedef struct SDL_error
         unsigned char value_c;
 #endif
         int value_i;
+        long value_l;
         double value_f;
         char buf[ERR_MAX_STRLEN];
     } args[ERR_MAX_ARGS];

+ 1 - 1
Source/ThirdParty/SDL/src/SDL_hints.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 6 - 2
Source/ThirdParty/SDL/src/SDL_internal.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -29,12 +29,16 @@
 /* This is for a variable-length array at the end of a struct:
     struct x { int y; char z[SDL_VARIABLE_LENGTH_ARRAY]; };
    Use this because GCC 2 needs different magic than other compilers. */
-#if (defined(__GNUC__) && (__GNUC__ <= 2)) || defined(__CC_ARM)
+#if (defined(__GNUC__) && (__GNUC__ <= 2)) || defined(__CC_ARM) || defined(__cplusplus)
 #define SDL_VARIABLE_LENGTH_ARRAY 1
 #else
 #define SDL_VARIABLE_LENGTH_ARRAY
 #endif
 
+#define SDL_MAX_SMALL_ALLOC_STACKSIZE 128
+#define SDL_small_alloc(type, count, pisstack) ( (*(pisstack) = ((sizeof(type)*(count)) < SDL_MAX_SMALL_ALLOC_STACKSIZE)), (*(pisstack) ? SDL_stack_alloc(type, count) : (type*)SDL_malloc(sizeof(type)*(count))) )
+#define SDL_small_free(ptr, isstack) if ((isstack)) { SDL_stack_free(ptr); } else { SDL_free(ptr); }
+
 #include "dynapi/SDL_dynapi.h"
 
 #if SDL_DYNAMIC_API

+ 21 - 8
Source/ThirdParty/SDL/src/SDL_log.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -282,6 +282,7 @@ SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list
         return;
     }
 
+    /* !!! FIXME: why not just "char message[SDL_MAX_LOG_MESSAGE];" ? */
     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
     if (!message) {
         return;
@@ -303,7 +304,7 @@ SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list
 }
 
 #if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__)
-/* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */
+/* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -1=error */
 static int consoleAttached = 0;
 
 /* Handle to stderr output of console. */
@@ -321,11 +322,13 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
         char *output;
         size_t length;
         LPTSTR tstr;
+        SDL_bool isstack;
 
 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
         BOOL attachResult;
         DWORD attachError;
         unsigned long charsWritten; 
+        DWORD consoleMode;
 
         /* Maybe attach console and get stderr handle */
         if (consoleAttached == 0) {
@@ -350,15 +353,20 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
                     /* Newly attached */
                     consoleAttached = 1;
                 }
-			
+
                 if (consoleAttached == 1) {
                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
+
+                        if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
+                            /* WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. */
+                            consoleAttached = 2;
+                        }
                 }
         }
 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
 
         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
-        output = SDL_stack_alloc(char, length);
+        output = SDL_small_alloc(char, length, &isstack);
         SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
         tstr = WIN_UTF8ToString(output);
         
@@ -374,11 +382,16 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
                         OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
                     }
                 }
+
+        } else if (consoleAttached == 2) {
+            if (!WriteFile(stderrHandle, output, lstrlenA(output), &charsWritten, NULL)) {
+                OutputDebugString(TEXT("Error calling WriteFile\r\n"));
+            }
         }
 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
 
         SDL_free(tstr);
-        SDL_stack_free(output);
+        SDL_small_free(output, isstack);
     }
 #elif defined(__ANDROID__)
     {
@@ -387,13 +400,13 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
         __android_log_write(SDL_android_priority[priority], tag, message);
     }
-#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
-    /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
+#elif defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))
+    /* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now.
     */
     extern void SDL_NSLog(const char *text);
     {
         char *text;
-
+        /* !!! FIXME: why not just "char text[SDL_MAX_LOG_MESSAGE];" ? */
         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
         if (text) {
             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);

+ 7 - 2
Source/ThirdParty/SDL/src/atomic/SDL_atomic.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -53,10 +53,11 @@
 #endif
 
 #if defined(__WATCOMC__) && defined(__386__)
+SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));
 #define HAVE_WATCOM_ATOMICS
 extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
 #pragma aux _SDL_xchg_watcom = \
-  "xchg [ecx], eax" \
+  "lock xchg [ecx], eax" \
   parm [ecx] [eax] \
   value [eax] \
   modify exact [eax];
@@ -288,6 +289,10 @@ SDL_AtomicGetPtr(void **a)
 #endif
 }
 
+#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION
+#error This file should be built in arm mode so the mcr instruction is available for memory barriers
+#endif
+
 void
 SDL_MemoryBarrierReleaseFunction(void)
 {

+ 27 - 3
Source/ThirdParty/SDL/src/atomic/SDL_spinlock.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -32,11 +32,15 @@
 #include <atomic.h>
 #endif
 
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include <xmmintrin.h>
+#endif
+
 #if defined(__WATCOMC__) && defined(__386__)
 SDL_COMPILE_TIME_ASSERT(locksize, 4==sizeof(SDL_SpinLock));
 extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
 #pragma aux _SDL_xchg_watcom = \
-  "xchg [ecx], eax" \
+  "lock xchg [ecx], eax" \
   parm [ecx] [eax] \
   value [eax] \
   modify exact [eax];
@@ -116,12 +120,32 @@ SDL_AtomicTryLock(SDL_SpinLock *lock)
 #endif
 }
 
+/* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
+#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
+    #define PAUSE_INSTRUCTION() __asm__ __volatile__("pause\n")  /* Some assemblers can't do REP NOP, so go with PAUSE. */
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+    #define PAUSE_INSTRUCTION() _mm_pause()  /* this is actually "rep nop" and not a SIMD instruction. No inline asm in MSVC x86-64! */
+#elif defined(__WATCOMC__) && defined(__386__)
+    /* watcom assembler rejects PAUSE if CPU < i686, and it refuses REP NOP as an invalid combination. Hardcode the bytes.  */
+    extern _inline void PAUSE_INSTRUCTION(void);
+    #pragma aux PAUSE_INSTRUCTION = "db 0f3h,90h"
+#else
+    #define PAUSE_INSTRUCTION()
+#endif
+
 void
 SDL_AtomicLock(SDL_SpinLock *lock)
 {
+    int iterations = 0;
     /* FIXME: Should we have an eventual timeout? */
     while (!SDL_AtomicTryLock(lock)) {
-        SDL_Delay(0);
+        if (iterations < 32) {
+            iterations++;
+            PAUSE_INSTRUCTION();
+        } else {
+            /* !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms. */
+            SDL_Delay(0);
+        }
     }
 }
 

+ 115 - 45
Source/ThirdParty/SDL/src/audio/SDL_audio.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -71,9 +71,6 @@ static const AudioBootStrap *const bootstrap[] = {
 #if SDL_AUDIO_DRIVER_WASAPI
     &WASAPI_bootstrap,
 #endif
-#if SDL_AUDIO_DRIVER_XAUDIO2
-    &XAUDIO2_bootstrap,
-#endif
 #if SDL_AUDIO_DRIVER_DSOUND
     &DSOUND_bootstrap,
 #endif
@@ -92,6 +89,9 @@ static const AudioBootStrap *const bootstrap[] = {
 #if SDL_AUDIO_DRIVER_FUSIONSOUND
     &FUSIONSOUND_bootstrap,
 #endif
+#if SDL_AUDIO_DRIVER_OPENSLES
+    &openslES_bootstrap,
+#endif
 #if SDL_AUDIO_DRIVER_ANDROID
     &ANDROIDAUDIO_bootstrap,
 #endif
@@ -234,19 +234,18 @@ SDL_AudioThreadDeinit_Default(_THIS)
 }
 
 static void
-SDL_AudioWaitDevice_Default(_THIS)
+SDL_AudioBeginLoopIteration_Default(_THIS)
 {                               /* no-op. */
 }
 
 static void
-SDL_AudioPlayDevice_Default(_THIS)
+SDL_AudioWaitDevice_Default(_THIS)
 {                               /* no-op. */
 }
 
-static int
-SDL_AudioGetPendingBytes_Default(_THIS)
-{
-    return 0;
+static void
+SDL_AudioPlayDevice_Default(_THIS)
+{                               /* no-op. */
 }
 
 static Uint8 *
@@ -353,9 +352,9 @@ finish_audio_entry_points_init(void)
     FILL_STUB(OpenDevice);
     FILL_STUB(ThreadInit);
     FILL_STUB(ThreadDeinit);
+    FILL_STUB(BeginLoopIteration);
     FILL_STUB(WaitDevice);
     FILL_STUB(PlayDevice);
-    FILL_STUB(GetPendingBytes);
     FILL_STUB(GetDeviceBuf);
     FILL_STUB(CaptureFromDevice);
     FILL_STUB(FlushCapture);
@@ -375,21 +374,57 @@ static int
 add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
 {
     int retval = -1;
-    const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1;
-    SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size);
-    if (item == NULL) {
-        return -1;
-    }
+    SDL_AudioDeviceItem *item;
+    const SDL_AudioDeviceItem *i;
+    int dupenum = 0;
 
     SDL_assert(handle != NULL);  /* we reserve NULL, audio backends can't use it. */
+    SDL_assert(name != NULL);
+
+    item = (SDL_AudioDeviceItem *) SDL_malloc(sizeof (SDL_AudioDeviceItem));
+    if (!item) {
+        return SDL_OutOfMemory();
+    }
+
+    item->original_name = SDL_strdup(name);
+    if (!item->original_name) {
+        SDL_free(item);
+        return SDL_OutOfMemory();
+    }
 
+    item->dupenum = 0;
+    item->name = item->original_name;
     item->handle = handle;
-    SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem));
 
     SDL_LockMutex(current_audio.detectionLock);
+
+    for (i = *devices; i != NULL; i = i->next) {
+        if (SDL_strcmp(name, i->original_name) == 0) {
+            dupenum = i->dupenum + 1;
+            break;  /* stop at the highest-numbered dupe. */
+        }
+    }
+
+    if (dupenum) {
+        const size_t len = SDL_strlen(name) + 16;
+        char *replacement = (char *) SDL_malloc(len);
+        if (!replacement) {
+            SDL_UnlockMutex(current_audio.detectionLock);
+            SDL_free(item->original_name);
+            SDL_free(item);
+            SDL_OutOfMemory();
+            return -1;
+        }
+
+        SDL_snprintf(replacement, len, "%s (%d)", name, dupenum + 1);
+        item->dupenum = dupenum;
+        item->name = replacement;
+    }
+
     item->next = *devices;
     *devices = item;
-    retval = (*devCount)++;
+    retval = (*devCount)++;   /* !!! FIXME: this should be an atomic increment */
+
     SDL_UnlockMutex(current_audio.detectionLock);
 
     return retval;
@@ -417,6 +452,11 @@ free_device_list(SDL_AudioDeviceItem **devices, int *devCount)
         if (item->handle != NULL) {
             current_audio.impl.FreeDeviceHandle(item->handle);
         }
+        /* these two pointers are the same if not a duplicate devname */
+        if (item->name != item->original_name) {
+            SDL_free(item->name);
+        }
+        SDL_free(item->original_name);
         SDL_free(item);
     }
     *devices = NULL;
@@ -448,7 +488,11 @@ void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
     SDL_assert(get_audio_device(device->id) == device);
 
     if (!SDL_AtomicGet(&device->enabled)) {
-        return;
+        return;  /* don't report disconnects more than once. */
+    }
+
+    if (SDL_AtomicGet(&device->shutdown)) {
+        return;  /* don't report disconnect if we're trying to close device. */
     }
 
     /* Ends the audio callback and mark the device as STOPPED, but the
@@ -603,11 +647,9 @@ SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
     }
 
     /* Nothing to do unless we're set up for queueing. */
-    if (device->callbackspec.callback == SDL_BufferQueueDrainCallback) {
-        current_audio.impl.LockDevice(device);
-        retval = ((Uint32) SDL_CountDataQueue(device->buffer_queue)) + current_audio.impl.GetPendingBytes(device);
-        current_audio.impl.UnlockDevice(device);
-    } else if (device->callbackspec.callback == SDL_BufferQueueFillCallback) {
+    if (device->callbackspec.callback == SDL_BufferQueueDrainCallback ||
+        device->callbackspec.callback == SDL_BufferQueueFillCallback)
+    {
         current_audio.impl.LockDevice(device);
         retval = (Uint32) SDL_CountDataQueue(device->buffer_queue);
         current_audio.impl.UnlockDevice(device);
@@ -642,12 +684,21 @@ SDL_RunAudio(void *devicep)
     SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
     void *udata = device->callbackspec.userdata;
     SDL_AudioCallback callback = device->callbackspec.callback;
+    int data_len = 0;
     Uint8 *data;
 
     SDL_assert(!device->iscapture);
 
+#if SDL_AUDIO_DRIVER_ANDROID
+    {
+        /* Set thread priority to THREAD_PRIORITY_AUDIO */
+        extern void Android_JNI_AudioSetThreadPriority(int, int);
+        Android_JNI_AudioSetThreadPriority(device->iscapture, device->id);
+    }
+#else
     /* The audio mixing is always a high priority thread */
-    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
+#endif
 
     /* Perform any thread setup */
     device->threadid = SDL_ThreadID();
@@ -655,7 +706,8 @@ SDL_RunAudio(void *devicep)
 
     /* Loop, filling the audio buffers */
     while (!SDL_AtomicGet(&device->shutdown)) {
-        const int data_len = device->callbackspec.size;
+        current_audio.impl.BeginLoopIteration(device);
+        data_len = device->callbackspec.size;
 
         /* Fill the current buffer with sound */
         if (!device->stream && SDL_AtomicGet(&device->enabled)) {
@@ -742,8 +794,16 @@ SDL_CaptureAudio(void *devicep)
 
     SDL_assert(device->iscapture);
 
+#if SDL_AUDIO_DRIVER_ANDROID
+    {
+        /* Set thread priority to THREAD_PRIORITY_AUDIO */
+        extern void Android_JNI_AudioSetThreadPriority(int, int);
+        Android_JNI_AudioSetThreadPriority(device->iscapture, device->id);
+    }
+#else
     /* The audio mixing is always a high priority thread */
     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+#endif
 
     /* Perform any thread setup */
     device->threadid = SDL_ThreadID();
@@ -754,6 +814,8 @@ SDL_CaptureAudio(void *devicep)
         int still_need;
         Uint8 *ptr;
 
+        current_audio.impl.BeginLoopIteration(device);
+
         if (SDL_AtomicGet(&device->paused)) {
             SDL_Delay(delay);  /* just so we don't cook the CPU. */
             if (device->stream) {
@@ -964,6 +1026,11 @@ clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *re
             } else {
                 *devices = next;
             }
+            /* these two pointers are the same if not a duplicate devname */
+            if (item->name != item->original_name) {
+                SDL_free(item->name);
+            }
+            SDL_free(item->original_name);
             SDL_free(item);
         }
         item = next;
@@ -990,7 +1057,6 @@ SDL_GetNumAudioDevices(int iscapture)
 
     if (!iscapture && current_audio.outputDevicesRemoved) {
         clean_out_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount, &current_audio.outputDevicesRemoved);
-        current_audio.outputDevicesRemoved = SDL_FALSE;
     }
 
     retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
@@ -1047,16 +1113,14 @@ close_audio_device(SDL_AudioDevice * device)
         return;
     }
 
-    if (device->id > 0) {
-        SDL_AudioDevice *opendev = open_devices[device->id - 1];
-        SDL_assert((opendev == device) || (opendev == NULL));
-        if (opendev == device) {
-            open_devices[device->id - 1] = NULL;
-        }
-    }
-
+    /* make sure the device is paused before we do anything else, so the
+       audio callback definitely won't fire again. */
+    current_audio.impl.LockDevice(device);
+    SDL_AtomicSet(&device->paused, 1);
     SDL_AtomicSet(&device->shutdown, 1);
     SDL_AtomicSet(&device->enabled, 0);
+    current_audio.impl.UnlockDevice(device);
+
     if (device->thread != NULL) {
         SDL_WaitThread(device->thread, NULL);
     }
@@ -1067,6 +1131,14 @@ close_audio_device(SDL_AudioDevice * device)
     SDL_free(device->work_buffer);
     SDL_FreeAudioStream(device->stream);
 
+    if (device->id > 0) {
+        SDL_AudioDevice *opendev = open_devices[device->id - 1];
+        SDL_assert((opendev == device) || (opendev == NULL));
+        if (opendev == device) {
+            open_devices[device->id - 1] = NULL;
+        }
+    }
+
     if (device->hidden != NULL) {
         current_audio.impl.CloseDevice(device);
     }
@@ -1111,8 +1183,9 @@ prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared)
         }
     case 1:                    /* Mono */
     case 2:                    /* Stereo */
-    case 4:                    /* surround */
-    case 6:                    /* surround with center and lfe */
+    case 4:                    /* Quadrophonic */
+    case 6:                    /* 5.1 surround */
+    case 8:                    /* 7.1 surround */
         break;
     default:
         SDL_SetError("Unsupported number of audio channels.");
@@ -1305,15 +1378,12 @@ open_audio_device(const char *devname, int iscapture,
             build_stream = SDL_TRUE;
         }
     }
-
-    /* !!! FIXME in 2.1: add SDL_AUDIO_ALLOW_SAMPLES_CHANGE flag?
-       As of 2.0.6, we will build a stream to buffer the difference between
-       what the app wants to feed and the device wants to eat, so everyone
-       gets their way. In prior releases, SDL would force the callback to
-       feed at the rate the device requested, adjusted for resampling.
-     */
     if (device->spec.samples != obtained->samples) {
-        build_stream = SDL_TRUE;
+        if (allowed_changes & SDL_AUDIO_ALLOW_SAMPLES_CHANGE) {
+            obtained->samples = device->spec.samples;
+        } else {
+            build_stream = SDL_TRUE;
+        }
     }
 
     SDL_CalculateAudioSpec(obtained);  /* recalc after possible changes. */

+ 1 - 1
Source/ThirdParty/SDL/src/audio/SDL_audio_c.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 18 - 8
Source/ThirdParty/SDL/src/audio/SDL_audiocvt.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -718,13 +718,19 @@ SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format
     /* !!! FIXME: remove this if we can get the resampler to work in-place again. */
     float *dst = (float *) (cvt->buf + srclen);
     const int dstlen = (cvt->len * cvt->len_mult) - srclen;
-    const int paddingsamples = (ResamplerPadding(inrate, outrate) * chans);
+    const int requestedpadding = ResamplerPadding(inrate, outrate);
+    int paddingsamples;
     float *padding;
 
+    if (requestedpadding < SDL_MAX_SINT32 / chans) {
+        paddingsamples = requestedpadding * chans;
+    } else {
+        paddingsamples = 0;
+    }
     SDL_assert(format == AUDIO_F32SYS);
 
     /* we keep no streaming state here, so pad with silence on both ends. */
-    padding = (float *) SDL_calloc(paddingsamples, sizeof (float));
+    padding = (float *) SDL_calloc(paddingsamples ? paddingsamples : 1, sizeof (float));
     if (!padding) {
         SDL_OutOfMemory();
         return;
@@ -889,10 +895,14 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
         return SDL_SetError("Invalid source channels");
     } else if (!SDL_SupportedChannelCount(dst_channels)) {
         return SDL_SetError("Invalid destination channels");
-    } else if (src_rate == 0) {
-        return SDL_SetError("Source rate is zero");
-    } else if (dst_rate == 0) {
-        return SDL_SetError("Destination rate is zero");
+    } else if (src_rate <= 0) {
+        return SDL_SetError("Source rate is equal to or less than zero");
+    } else if (dst_rate <= 0) {
+        return SDL_SetError("Destination rate is equal to or less than zero");
+    } else if (src_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) {
+        return SDL_SetError("Source rate is too high");
+    } else if (dst_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) {
+        return SDL_SetError("Destination rate is too high");
     }
 
 #if DEBUG_CONVERT
@@ -1291,7 +1301,7 @@ SDL_NewAudioStream(const SDL_AudioFormat src_format,
     retval->packetlen = packetlen;
     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
     retval->resampler_padding_samples = ResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels;
-    retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples, sizeof (float));
+    retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples ? retval->resampler_padding_samples : 1, sizeof (float));
 
     if (retval->resampler_padding == NULL) {
         SDL_FreeAudioStream(retval);

+ 1 - 1
Source/ThirdParty/SDL/src/audio/SDL_audiodev.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 7 - 1
Source/ThirdParty/SDL/src/audio/SDL_audiodev_c.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_audiodev_c_h_
+#define SDL_audiodev_c_h_
+
 #include "SDL.h"
 #include "../SDL_internal.h"
 #include "SDL_sysaudio.h"
@@ -35,4 +39,6 @@
 
 extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int));
 
+#endif /* SDL_audiodev_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */

+ 671 - 60
Source/ThirdParty/SDL/src/audio/SDL_audiotypecvt.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -25,8 +25,9 @@
 #include "SDL_cpuinfo.h"
 #include "SDL_assert.h"
 
-/* !!! FIXME: write NEON code. */
-#define HAVE_NEON_INTRINSICS 0
+#ifdef __ARM_NEON
+#define HAVE_NEON_INTRINSICS 1
+#endif
 
 #ifdef __SSE2__
 #define HAVE_SSE2_INTRINSICS 1
@@ -62,7 +63,7 @@ SDL_AudioFilter SDL_Convert_F32_to_S32 = NULL;
 
 #define DIVBY128 0.0078125f
 #define DIVBY32768 0.000030517578125f
-#define DIVBY2147483648 0.00000000046566128730773926
+#define DIVBY8388607 0.00000011920930376163766f
 
 
 #if NEED_SCALAR_CONVERTER_FALLBACKS
@@ -152,7 +153,7 @@ SDL_Convert_S32_to_F32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
     LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32");
 
     for (i = cvt->len_cvt / sizeof (Sint32); i; --i, ++src, ++dst) {
-        *dst = (float) (((double) *src) * DIVBY2147483648);
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
     }
 
     if (cvt->filters[++cvt->filter_index]) {
@@ -171,10 +172,10 @@ SDL_Convert_F32_to_S8_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 127;
-        } else if (sample < -1.0f) {
-            *dst = -127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
         } else {
             *dst = (Sint8)(sample * 127.0f);
         }
@@ -197,9 +198,9 @@ SDL_Convert_F32_to_U8_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 255;
-        } else if (sample < -1.0f) {
+        } else if (sample <= -1.0f) {
             *dst = 0;
         } else {
             *dst = (Uint8)((sample + 1.0f) * 127.0f);
@@ -223,10 +224,10 @@ SDL_Convert_F32_to_S16_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 32767;
-        } else if (sample < -1.0f) {
-            *dst = -32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
         } else {
             *dst = (Sint16)(sample * 32767.0f);
         }
@@ -249,9 +250,9 @@ SDL_Convert_F32_to_U16_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
-            *dst = 65534;
-        } else if (sample < -1.0f) {
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
             *dst = 0;
         } else {
             *dst = (Uint16)((sample + 1.0f) * 32767.0f);
@@ -275,12 +276,12 @@ SDL_Convert_F32_to_S32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 2147483647;
-        } else if (sample < -1.0f) {
-            *dst = -2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (Sint32) -2147483648LL;
         } else {
-            *dst = (Sint32)((double)sample * 2147483647.0);
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
         }
     }
 
@@ -520,23 +521,19 @@ SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (Sint32); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (float) (((double) *src) * DIVBY2147483648);
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
-    SDL_assert(!i || ((((size_t) src) & 15) == 0));
 
-    {
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
-        const __m128d divby2147483648 = _mm_set1_pd(DIVBY2147483648);
+        const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607);
         const __m128i *mmsrc = (const __m128i *) src;
         while (i >= 4) {   /* 4 * sint32 */
-            const __m128i ints = _mm_load_si128(mmsrc);
-            /* bitshift the whole register over, so _mm_cvtepi32_pd can read the top ints in the bottom of the vector. */
-            const __m128d doubles1 = _mm_mul_pd(_mm_cvtepi32_pd(_mm_srli_si128(ints, 8)), divby2147483648);
-            const __m128d doubles2 = _mm_mul_pd(_mm_cvtepi32_pd(ints), divby2147483648);
-            /* convert to float32, bitshift/or to get these into a vector to store. */
-            _mm_store_ps(dst, _mm_castsi128_ps(_mm_or_si128(_mm_slli_si128(_mm_castps_si128(_mm_cvtpd_ps(doubles1)), 8), _mm_castps_si128(_mm_cvtpd_ps(doubles2)))));
+            /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
+            _mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607));
             i -= 4; mmsrc++; dst += 4;
         }
         src = (const Sint32 *) mmsrc;
@@ -544,7 +541,7 @@ SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (float) (((double) *src) * DIVBY2147483648);
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
         i--; src++; dst++;
     }
 
@@ -564,7 +561,14 @@ SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Sint8) (*src * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -572,13 +576,15 @@ SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
     /* Make sure src is aligned too. */
     if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         const __m128 mulby127 = _mm_set1_ps(127.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 16) {   /* 16 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+8), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+12), mulby127));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+8)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+12)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_packs_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4)));  /* pack down, store out. */
             i -= 16; src += 16; mmdst++;
         }
@@ -587,7 +593,14 @@ SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Sint8) (*src * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -608,7 +621,14 @@ SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Uint8) ((*src + 1.0f) * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -616,14 +636,15 @@ SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
     /* Make sure src is aligned too. */
     if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
-        const __m128 add1 = _mm_set1_ps(1.0f);
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         const __m128 mulby127 = _mm_set1_ps(127.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 16) {   /* 16 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src), add1), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+4), add1), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+8), add1), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+12), add1), mulby127));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+8)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+12)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_packus_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4)));  /* pack down, store out. */
             i -= 16; src += 16; mmdst++;
         }
@@ -632,7 +653,14 @@ SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Uint8) ((*src + 1.0f) * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -653,7 +681,14 @@ SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Sint16) (*src * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -661,11 +696,13 @@ SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
     /* Make sure src is aligned too. */
     if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         const __m128 mulby32767 = _mm_set1_ps(32767.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 8) {   /* 8 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby32767));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby32767));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_packs_epi32(ints1, ints2));  /* pack to sint16, store out. */
             i -= 8; src += 8; mmdst++;
         }
@@ -674,7 +711,14 @@ SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Sint16) (*src * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -695,7 +739,14 @@ SDL_Convert_F32_to_U16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Uint16) ((*src + 1.0f) * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -712,10 +763,12 @@ SDL_Convert_F32_to_U16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
            though it looks like dark magic. */
         const __m128 mulby32767 = _mm_set1_ps(32767.0f);
         const __m128i topbit = _mm_set1_epi16(-32768);
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 8) {   /* 8 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby32767));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby32767));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_xor_si128(_mm_packs_epi32(ints1, ints2), topbit));  /* pack to sint16, xor top bit, store out. */
             i -= 8; src += 8; mmdst++;
         }
@@ -724,7 +777,14 @@ SDL_Convert_F32_to_U16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Uint16) ((*src + 1.0f) * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -745,7 +805,14 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Sint32) (((double) *src) * 2147483647.0);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (Sint32) -2147483648LL;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -753,14 +820,12 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
-        const __m128d mulby2147483647 = _mm_set1_pd(2147483647.0);
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
+        const __m128 mulby8388607 = _mm_set1_ps(8388607.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 4) {   /* 4 * float32 */
-            const __m128 floats = _mm_load_ps(src);
-            /* bitshift the whole register over, so _mm_cvtps_pd can read the top floats in the bottom of the vector. */
-            const __m128d doubles1 = _mm_mul_pd(_mm_cvtps_pd(_mm_castsi128_ps(_mm_srli_si128(_mm_castps_si128(floats), 8))), mulby2147483647);
-            const __m128d doubles2 = _mm_mul_pd(_mm_cvtps_pd(floats), mulby2147483647);
-            _mm_store_si128(mmdst, _mm_or_si128(_mm_slli_si128(_mm_cvtpd_epi32(doubles1), 8), _mm_cvtpd_epi32(doubles2)));
+            _mm_store_si128(mmdst, _mm_slli_epi32(_mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby8388607)), 8));  /* load 4 floats, clamp, convert to sint32 */
             i -= 4; src += 4; mmdst++;
         }
         dst = (Sint32 *) mmdst;
@@ -768,7 +833,14 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Sint32) (((double) *src) * 2147483647.0);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (Sint32) -2147483648LL;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
         i--; src++; dst++;
     }
 
@@ -779,6 +851,538 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 #endif
 
 
+#if HAVE_NEON_INTRINSICS
+static void SDLCALL
+SDL_Convert_S8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Sint8 *src = ((const Sint8 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 4)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_S8", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) {
+        *dst = ((float) *src) * DIVBY128;
+    }
+
+    src -= 15; dst -= 15;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const int8_t *mmsrc = (const int8_t *) src;
+        const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
+        while (i >= 16) {   /* 16 * 8-bit */
+            const int8x16_t bytes = vld1q_s8(mmsrc);  /* get 16 sint8 into a NEON register. */
+            const int16x8_t int16hi = vmovl_s8(vget_high_s8(bytes));  /* convert top 8 bytes to 8 int16 */
+            const int16x8_t int16lo = vmovl_s8(vget_low_s8(bytes));   /* convert bottom 8 bytes to 8 int16 */
+            /* split int16 to two int32, then convert to float, then multiply to normalize, store. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16hi))), divby128));
+            vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16hi))), divby128));
+            vst1q_f32(dst+8, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16lo))), divby128));
+            vst1q_f32(dst+12, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16lo))), divby128));
+            i -= 16; mmsrc -= 16; dst -= 16;
+        }
+
+        src = (const Sint8 *) mmsrc;
+    }
+
+    src += 15; dst += 15;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) *src) * DIVBY128;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_U8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Uint8 *src = ((const Uint8 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 4)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_U8", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+    }
+
+    src -= 15; dst -= 15;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const uint8_t *mmsrc = (const uint8_t *) src;
+        const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        while (i >= 16) {   /* 16 * 8-bit */
+            const uint8x16_t bytes = vld1q_u8(mmsrc);  /* get 16 uint8 into a NEON register. */
+            const uint16x8_t uint16hi = vmovl_u8(vget_high_u8(bytes));  /* convert top 8 bytes to 8 uint16 */
+            const uint16x8_t uint16lo = vmovl_u8(vget_low_u8(bytes));   /* convert bottom 8 bytes to 8 uint16 */
+            /* split uint16 to two uint32, then convert to float, then multiply to normalize, subtract to adjust for sign, store. */
+            vst1q_f32(dst, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16hi))), divby128));
+            vst1q_f32(dst+4, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16hi))), divby128));
+            vst1q_f32(dst+8, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16lo))), divby128));
+            vst1q_f32(dst+12, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16lo))), divby128));
+            i -= 16; mmsrc -= 16; dst -= 16;
+        }
+
+        src = (const Uint8 *) mmsrc;
+    }
+
+    src += 15; dst += 15;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_S16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Sint16 *src = ((const Sint16 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 2)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_S16", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt / sizeof (Sint16); i && (((size_t) (dst-7)) & 15); --i, --src, --dst) {
+        *dst = ((float) *src) * DIVBY32768;
+    }
+
+    src -= 7; dst -= 7;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768);
+        while (i >= 8) {   /* 8 * 16-bit */
+            const int16x8_t ints = vld1q_s16((int16_t const *) src);  /* get 8 sint16 into a NEON register. */
+            /* split int16 to two int32, then convert to float, then multiply to normalize, store. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(ints))), divby32768));
+            vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(ints))), divby32768));
+            i -= 8; src -= 8; dst -= 8;
+        }
+    }
+
+    src += 7; dst += 7;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) *src) * DIVBY32768;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_U16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Uint16 *src = ((const Uint16 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 2)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_U16", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt / sizeof (Sint16); i && (((size_t) (dst-7)) & 15); --i, --src, --dst) {
+        *dst = (((float) *src) * DIVBY32768) - 1.0f;
+    }
+
+    src -= 7; dst -= 7;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        while (i >= 8) {   /* 8 * 16-bit */
+            const uint16x8_t uints = vld1q_u16((uint16_t const *) src);  /* get 8 uint16 into a NEON register. */
+            /* split uint16 to two int32, then convert to float, then multiply to normalize, subtract for sign, store. */
+            vst1q_f32(dst, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uints))), divby32768));
+            vst1q_f32(dst+4, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uints))), divby32768));
+            i -= 8; src -= 8; dst -= 8;
+        }
+    }
+
+    src += 7; dst += 7;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = (((float) *src) * DIVBY32768) - 1.0f;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_S32_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Sint32 *src = (const Sint32 *) cvt->buf;
+    float *dst = (float *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (Sint32); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby8388607 = vdupq_n_f32(DIVBY8388607);
+        const int32_t *mmsrc = (const int32_t *) src;
+        while (i >= 4) {   /* 4 * sint32 */
+            /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vshrq_n_s32(vld1q_s32(mmsrc), 8)), divby8388607));
+            i -= 4; mmsrc += 4; dst += 4;
+        }
+        src = (const Sint32 *) mmsrc;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+        i--; src++; dst++;
+    }
+
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_S8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Sint8 *dst = (Sint8 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S8 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby127 = vdupq_n_f32(127.0f);
+        int8_t *mmdst = (int8_t *) dst;
+        while (i >= 16) {   /* 16 * float32 */
+            const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints3 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+8)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints4 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+12)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int8x8_t i8lo = vmovn_s16(vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2))); /* narrow to sint16, combine, narrow to sint8 */
+            const int8x8_t i8hi = vmovn_s16(vcombine_s16(vmovn_s32(ints3), vmovn_s32(ints4))); /* narrow to sint16, combine, narrow to sint8 */
+            vst1q_s8(mmdst, vcombine_s8(i8lo, i8hi));  /* combine to int8x16_t, store out */
+            i -= 16; src += 16; mmdst += 16;
+        }
+        dst = (Sint8 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_S8);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_U8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Uint8 *dst = (Uint8 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U8 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby127 = vdupq_n_f32(127.0f);
+        uint8_t *mmdst = (uint8_t *) dst;
+        while (i >= 16) {   /* 16 * float32 */
+            const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints3 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+8)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints4 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+12)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint8x8_t ui8lo = vmovn_u16(vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2))); /* narrow to uint16, combine, narrow to uint8 */
+            const uint8x8_t ui8hi = vmovn_u16(vcombine_u16(vmovn_u32(uints3), vmovn_u32(uints4))); /* narrow to uint16, combine, narrow to uint8 */
+            vst1q_u8(mmdst, vcombine_u8(ui8lo, ui8hi));  /* combine to uint8x16_t, store out */
+            i -= 16; src += 16; mmdst += 16;
+        }
+
+        dst = (Uint8 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_U8);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_S16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Sint16 *dst = (Sint16 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S16 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby32767 = vdupq_n_f32(32767.0f);
+        int16_t *mmdst = (int16_t *) dst;
+        while (i >= 8) {   /* 8 * float32 */
+            const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            vst1q_s16(mmdst, vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2)));  /* narrow to sint16, combine, store out. */
+            i -= 8; src += 8; mmdst += 8;
+        }
+        dst = (Sint16 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_S16SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_U16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Uint16 *dst = (Uint16 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U16 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby32767 = vdupq_n_f32(32767.0f);
+        uint16_t *mmdst = (uint16_t *) dst;
+        while (i >= 8) {   /* 8 * float32 */
+            const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby32767));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), one), mulby32767));  /* load 4 floats, clamp, convert to uint32 */
+            vst1q_u16(mmdst, vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2)));  /* narrow to uint16, combine, store out. */
+            i -= 8; src += 8; mmdst += 8;
+        }
+        dst = (Uint16 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_U16SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_S32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Sint32 *dst = (Sint32 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (-2147483647) - 1;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+    SDL_assert(!i || ((((size_t) src) & 15) == 0));
+
+    {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby8388607 = vdupq_n_f32(8388607.0f);
+        int32_t *mmdst = (int32_t *) dst;
+        while (i >= 4) {   /* 4 * float32 */
+            vst1q_s32(mmdst, vshlq_n_s32(vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby8388607)), 8));
+            i -= 4; src += 4; mmdst += 4;
+        }
+        dst = (Sint32 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (-2147483647) - 1;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
+        i--; src++; dst++;
+    }
+
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_S32SYS);
+    }
+}
+#endif
+
+
+
 void SDL_ChooseAudioConverters(void)
 {
     static SDL_bool converters_chosen = SDL_FALSE;
@@ -807,6 +1411,13 @@ void SDL_ChooseAudioConverters(void)
     }
 #endif
 
+#if HAVE_NEON_INTRINSICS
+    if (SDL_HasNEON()) {
+        SET_CONVERTER_FUNCS(NEON);
+        return;
+    }
+#endif
+
 #if NEED_SCALAR_CONVERTER_FALLBACKS
     SET_CONVERTER_FUNCS(Scalar);
 #endif

+ 1 - 1
Source/ThirdParty/SDL/src/audio/SDL_mixer.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages

+ 6 - 4
Source/ThirdParty/SDL/src/audio/SDL_sysaudio.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -68,9 +68,9 @@ typedef struct SDL_AudioDriverImpl
     int (*OpenDevice) (_THIS, void *handle, const char *devname, int iscapture);
     void (*ThreadInit) (_THIS); /* Called by audio thread at start */
     void (*ThreadDeinit) (_THIS); /* Called by audio thread at end */
+    void (*BeginLoopIteration)(_THIS);  /* Called by audio thread at top of loop */
     void (*WaitDevice) (_THIS);
     void (*PlayDevice) (_THIS);
-    int (*GetPendingBytes) (_THIS);
     Uint8 *(*GetDeviceBuf) (_THIS);
     int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
     void (*FlushCapture) (_THIS);
@@ -97,8 +97,10 @@ typedef struct SDL_AudioDriverImpl
 typedef struct SDL_AudioDeviceItem
 {
     void *handle;
+    char *name;
+    char *original_name;
+    int dupenum;
     struct SDL_AudioDeviceItem *next;
-    char name[SDL_VARIABLE_LENGTH_ARRAY];
 } SDL_AudioDeviceItem;
 
 
@@ -193,7 +195,6 @@ extern AudioBootStrap ESD_bootstrap;
 extern AudioBootStrap NACLAUDIO_bootstrap;
 extern AudioBootStrap NAS_bootstrap;
 extern AudioBootStrap WASAPI_bootstrap;
-extern AudioBootStrap XAUDIO2_bootstrap;
 extern AudioBootStrap DSOUND_bootstrap;
 extern AudioBootStrap WINMM_bootstrap;
 extern AudioBootStrap PAUDIO_bootstrap;
@@ -202,6 +203,7 @@ extern AudioBootStrap COREAUDIO_bootstrap;
 extern AudioBootStrap DISKAUDIO_bootstrap;
 extern AudioBootStrap DUMMYAUDIO_bootstrap;
 extern AudioBootStrap FUSIONSOUND_bootstrap;
+extern AudioBootStrap openslES_bootstrap;
 extern AudioBootStrap ANDROIDAUDIO_bootstrap;
 extern AudioBootStrap PSPAUDIO_bootstrap;
 extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;

+ 2025 - 566
Source/ThirdParty/SDL/src/audio/SDL_wave.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -20,675 +20,2134 @@
 */
 #include "../SDL_internal.h"
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#else
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+#ifndef INT_MAX
+/* Make a lucky guess. */
+#define INT_MAX SDL_MAX_SINT32
+#endif
+#endif
+
 /* Microsoft WAVE file loading routines */
 
-#include "SDL_audio.h"
-#include "SDL_wave.h"
+#include "SDL_log.h"
+#include "SDL_hints.h"
+#include "SDL_audio.h"
+#include "SDL_wave.h"
+
+/* Reads the value stored at the location of the f1 pointer, multiplies it
+ * with the second argument and then stores the result to f1.
+ * Returns 0 on success, or -1 if the multiplication overflows, in which case f1
+ * does not get modified.
+ */
+static int
+SafeMult(size_t *f1, size_t f2)
+{
+    if (*f1 > 0 && SIZE_MAX / *f1 <= f2) {
+        return -1;
+    }
+    *f1 *= f2;
+    return 0;
+}
+
+typedef struct ADPCM_DecoderState
+{
+    Uint32 channels;        /* Number of channels. */
+    size_t blocksize;       /* Size of an ADPCM block in bytes. */
+    size_t blockheadersize; /* Size of an ADPCM block header in bytes. */
+    size_t samplesperblock; /* Number of samples per channel in an ADPCM block. */
+    size_t framesize;       /* Size of a sample frame (16-bit PCM) in bytes. */
+    Sint64 framestotal;     /* Total number of sample frames. */
+    Sint64 framesleft;      /* Number of sample frames still to be decoded. */
+    void *ddata;            /* Decoder data from initialization. */
+    void *cstate;           /* Decoding state for each channel. */
+
+    /* ADPCM data. */
+    struct {
+        Uint8 *data;
+        size_t size;
+        size_t pos;
+    } input;
+
+    /* Current ADPCM block in the ADPCM data above. */
+    struct {
+        Uint8 *data;
+        size_t size;
+        size_t pos;
+    } block;
+
+    /* Decoded 16-bit PCM data. */
+    struct {
+        Sint16 *data;
+        size_t size;
+        size_t pos;
+    } output;
+} ADPCM_DecoderState;
+
+typedef struct MS_ADPCM_CoeffData
+{
+    Uint16 coeffcount;
+    Sint16 *coeff;
+    Sint16 aligndummy; /* Has to be last member. */
+} MS_ADPCM_CoeffData;
+
+typedef struct MS_ADPCM_ChannelState
+{
+    Uint16 delta;
+    Sint16 coeff1;
+    Sint16 coeff2;
+} MS_ADPCM_ChannelState;
+
+#ifdef SDL_WAVE_DEBUG_LOG_FORMAT
+static void
+WaveDebugLogFormat(WaveFile *file)
+{
+    WaveFormat *format = &file->format;
+    const char *fmtstr = "WAVE file: %s, %u Hz, %s, %u bits, %u %s/s";
+    const char *waveformat, *wavechannel, *wavebpsunit = "B";
+    Uint32 wavebps = format->byterate;
+    char channelstr[64];
+
+    SDL_zero(channelstr);
+
+    switch (format->encoding) {
+    case PCM_CODE:
+        waveformat = "PCM";
+        break;
+    case IEEE_FLOAT_CODE:
+        waveformat = "IEEE Float";
+        break;
+    case ALAW_CODE:
+        waveformat = "A-law";
+        break;
+    case MULAW_CODE:
+        waveformat = "\xc2\xb5-law";
+        break;
+    case MS_ADPCM_CODE:
+        waveformat = "MS ADPCM";
+        break;
+    case IMA_ADPCM_CODE:
+        waveformat = "IMA ADPCM";
+        break;
+    default:
+        waveformat = "Unknown";
+        break;
+    }
+
+#define SDL_WAVE_DEBUG_CHANNELCFG(STR, CODE) case CODE: wavechannel = STR; break;
+#define SDL_WAVE_DEBUG_CHANNELSTR(STR, CODE) if (format->channelmask & CODE) { \
+    SDL_strlcat(channelstr, channelstr[0] ? "-" STR : STR, sizeof(channelstr));}
+
+    if (format->formattag == EXTENSIBLE_CODE && format->channelmask > 0) {
+        switch (format->channelmask) {
+            SDL_WAVE_DEBUG_CHANNELCFG("1.0 Mono",         0x4)
+            SDL_WAVE_DEBUG_CHANNELCFG("1.1 Mono",         0xc)
+            SDL_WAVE_DEBUG_CHANNELCFG("2.0 Stereo",       0x3)
+            SDL_WAVE_DEBUG_CHANNELCFG("2.1 Stereo",       0xb)
+            SDL_WAVE_DEBUG_CHANNELCFG("3.0 Stereo",       0x7)
+            SDL_WAVE_DEBUG_CHANNELCFG("3.1 Stereo",       0xf)
+            SDL_WAVE_DEBUG_CHANNELCFG("3.0 Surround",     0x103)
+            SDL_WAVE_DEBUG_CHANNELCFG("3.1 Surround",     0x10b)
+            SDL_WAVE_DEBUG_CHANNELCFG("4.0 Quad",         0x33)
+            SDL_WAVE_DEBUG_CHANNELCFG("4.1 Quad",         0x3b)
+            SDL_WAVE_DEBUG_CHANNELCFG("4.0 Surround",     0x107)
+            SDL_WAVE_DEBUG_CHANNELCFG("4.1 Surround",     0x10f)
+            SDL_WAVE_DEBUG_CHANNELCFG("5.0",              0x37)
+            SDL_WAVE_DEBUG_CHANNELCFG("5.1",              0x3f)
+            SDL_WAVE_DEBUG_CHANNELCFG("5.0 Side",         0x607)
+            SDL_WAVE_DEBUG_CHANNELCFG("5.1 Side",         0x60f)
+            SDL_WAVE_DEBUG_CHANNELCFG("6.0",              0x137)
+            SDL_WAVE_DEBUG_CHANNELCFG("6.1",              0x13f)
+            SDL_WAVE_DEBUG_CHANNELCFG("6.0 Side",         0x707)
+            SDL_WAVE_DEBUG_CHANNELCFG("6.1 Side",         0x70f)
+            SDL_WAVE_DEBUG_CHANNELCFG("7.0",              0xf7)
+            SDL_WAVE_DEBUG_CHANNELCFG("7.1",              0xff)
+            SDL_WAVE_DEBUG_CHANNELCFG("7.0 Side",         0x6c7)
+            SDL_WAVE_DEBUG_CHANNELCFG("7.1 Side",         0x6cf)
+            SDL_WAVE_DEBUG_CHANNELCFG("7.0 Surround",     0x637)
+            SDL_WAVE_DEBUG_CHANNELCFG("7.1 Surround",     0x63f)
+            SDL_WAVE_DEBUG_CHANNELCFG("9.0 Surround",     0x5637)
+            SDL_WAVE_DEBUG_CHANNELCFG("9.1 Surround",     0x563f)
+            SDL_WAVE_DEBUG_CHANNELCFG("11.0 Surround",    0x56f7)
+            SDL_WAVE_DEBUG_CHANNELCFG("11.1 Surround",    0x56ff)
+        default:
+            SDL_WAVE_DEBUG_CHANNELSTR("FL",  0x1)
+            SDL_WAVE_DEBUG_CHANNELSTR("FR",  0x2)
+            SDL_WAVE_DEBUG_CHANNELSTR("FC",  0x4)
+            SDL_WAVE_DEBUG_CHANNELSTR("LF",  0x8)
+            SDL_WAVE_DEBUG_CHANNELSTR("BL",  0x10)
+            SDL_WAVE_DEBUG_CHANNELSTR("BR",  0x20)
+            SDL_WAVE_DEBUG_CHANNELSTR("FLC", 0x40)
+            SDL_WAVE_DEBUG_CHANNELSTR("FRC", 0x80)
+            SDL_WAVE_DEBUG_CHANNELSTR("BC",  0x100)
+            SDL_WAVE_DEBUG_CHANNELSTR("SL",  0x200)
+            SDL_WAVE_DEBUG_CHANNELSTR("SR",  0x400)
+            SDL_WAVE_DEBUG_CHANNELSTR("TC",  0x800)
+            SDL_WAVE_DEBUG_CHANNELSTR("TFL", 0x1000)
+            SDL_WAVE_DEBUG_CHANNELSTR("TFC", 0x2000)
+            SDL_WAVE_DEBUG_CHANNELSTR("TFR", 0x4000)
+            SDL_WAVE_DEBUG_CHANNELSTR("TBL", 0x8000)
+            SDL_WAVE_DEBUG_CHANNELSTR("TBC", 0x10000)
+            SDL_WAVE_DEBUG_CHANNELSTR("TBR", 0x20000)
+            break;
+        }
+    } else {
+        switch (format->channels) {
+        default:
+            if (SDL_snprintf(channelstr, sizeof(channelstr), "%u channels", format->channels) >= 0) {
+                wavechannel = channelstr;
+                break;
+            }
+        case 0:
+            wavechannel = "Unknown";
+            break;
+        case 1:
+            wavechannel = "Mono";
+            break;
+        case 2:
+            wavechannel = "Setero";
+            break;
+        }
+    }
+
+#undef SDL_WAVE_DEBUG_CHANNELCFG
+#undef SDL_WAVE_DEBUG_CHANNELSTR
+
+    if (wavebps >= 1024) {
+        wavebpsunit = "KiB";
+        wavebps = wavebps / 1024 + (wavebps & 0x3ff ? 1 : 0);
+    }
+
+    SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, fmtstr, waveformat, format->frequency, wavechannel, format->bitspersample, wavebps, wavebpsunit);
+}
+#endif
+
+#ifdef SDL_WAVE_DEBUG_DUMP_FORMAT
+static void
+WaveDebugDumpFormat(WaveFile *file, Uint32 rifflen, Uint32 fmtlen, Uint32 datalen)
+{
+    WaveFormat *format = &file->format;
+    const char *fmtstr1 = "WAVE chunk dump:\n"
+        "-------------------------------------------\n"
+        "RIFF                            %11u\n"
+        "-------------------------------------------\n"
+        "    fmt                         %11u\n"
+        "        wFormatTag                   0x%04x\n"
+        "        nChannels               %11u\n"
+        "        nSamplesPerSec          %11u\n"
+        "        nAvgBytesPerSec         %11u\n"
+        "        nBlockAlign             %11u\n";
+    const char *fmtstr2 = "        wBitsPerSample          %11u\n";
+    const char *fmtstr3 = "        cbSize                  %11u\n";
+    const char *fmtstr4a = "        wValidBitsPerSample     %11u\n";
+    const char *fmtstr4b = "        wSamplesPerBlock        %11u\n";
+    const char *fmtstr5 = "        dwChannelMask            0x%08x\n"
+        "        SubFormat\n"
+        "        %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n";
+    const char *fmtstr6 = "-------------------------------------------\n"
+        " fact\n"
+        "  dwSampleLength                %11u\n";
+    const char *fmtstr7 = "-------------------------------------------\n"
+        " data                           %11u\n"
+        "-------------------------------------------\n";
+    char *dumpstr;
+    size_t dumppos = 0;
+    const size_t bufsize = 1024;
+    int res;
+
+    dumpstr = SDL_malloc(bufsize);
+    if (dumpstr == NULL) {
+        return;
+    }
+    dumpstr[0] = 0;
+
+    res = SDL_snprintf(dumpstr, bufsize, fmtstr1, rifflen, fmtlen, format->formattag, format->channels, format->frequency, format->byterate, format->blockalign);
+    dumppos += res > 0 ? res : 0;
+    if (fmtlen >= 16) {
+        res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr2, format->bitspersample);
+        dumppos += res > 0 ? res : 0;
+    }
+    if (fmtlen >= 18) {
+        res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr3, format->extsize);
+        dumppos += res > 0 ? res : 0;
+    }
+    if (format->formattag == EXTENSIBLE_CODE && fmtlen >= 40 && format->extsize >= 22) {
+        const Uint8 *g = format->subformat;
+        const Uint32 g1 = g[0] | ((Uint32)g[1] << 8) | ((Uint32)g[2] << 16) | ((Uint32)g[3] << 24);
+        const Uint32 g2 = g[4] | ((Uint32)g[5] << 8);
+        const Uint32 g3 = g[6] | ((Uint32)g[7] << 8);
+
+        switch (format->encoding) {
+        default:
+            res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr4a, format->validsamplebits);
+            dumppos += res > 0 ? res : 0;
+            break;
+        case MS_ADPCM_CODE:
+        case IMA_ADPCM_CODE:
+            res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr4b, format->samplesperblock);
+            dumppos += res > 0 ? res : 0;
+            break;
+        }
+        res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr5, format->channelmask, g1, g2, g3, g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15]);
+        dumppos += res > 0 ? res : 0;
+    } else {
+        switch (format->encoding) {
+        case MS_ADPCM_CODE:
+        case IMA_ADPCM_CODE:
+            if (fmtlen >= 20 && format->extsize >= 2) {
+                res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr4b, format->samplesperblock);
+                dumppos += res > 0 ? res : 0;
+            }
+            break;
+        }
+    }
+    if (file->fact.status >= 1) {
+        res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr6, file->fact.samplelength);
+        dumppos += res > 0 ? res : 0;
+    }
+    res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr7, datalen);
+    dumppos += res > 0 ? res : 0;
+
+    SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", dumpstr);
+
+    free(dumpstr);
+}
+#endif
+
+static Sint64
+WaveAdjustToFactValue(WaveFile *file, Sint64 sampleframes)
+{
+    if (file->fact.status == 2) {
+        if (file->facthint == FactStrict && sampleframes < file->fact.samplelength) {
+            return SDL_SetError("Invalid number of sample frames in WAVE fact chunk (too many)");
+        } else if (sampleframes > file->fact.samplelength) {
+            return file->fact.samplelength;
+        }
+    }
+
+    return sampleframes;
+}
+
+static int
+MS_ADPCM_CalculateSampleFrames(WaveFile *file, size_t datalength)
+{
+    WaveFormat *format = &file->format;
+    const size_t blockheadersize = (size_t)file->format.channels * 7;
+    const size_t availableblocks = datalength / file->format.blockalign;
+    const size_t blockframebitsize = (size_t)file->format.bitspersample * file->format.channels;
+    const size_t trailingdata = datalength % file->format.blockalign;
+
+    if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) {
+        /* The size of the data chunk must be a multiple of the block size. */
+        if (datalength < blockheadersize || trailingdata > 0) {
+            return SDL_SetError("Truncated MS ADPCM block");
+        }
+    }
+
+    /* Calculate number of sample frames that will be decoded. */
+    file->sampleframes = (Sint64)availableblocks * format->samplesperblock;
+    if (trailingdata > 0) {
+        /* The last block is truncated. Check if we can get any samples out of it. */
+        if (file->trunchint == TruncDropFrame) {
+            /* Drop incomplete sample frame. */
+            if (trailingdata >= blockheadersize) {
+                size_t trailingsamples = 2 + (trailingdata - blockheadersize) * 8 / blockframebitsize;
+                if (trailingsamples > format->samplesperblock) {
+                    trailingsamples = format->samplesperblock;
+                }
+                file->sampleframes += trailingsamples;
+            }
+        }
+    }
+
+    file->sampleframes = WaveAdjustToFactValue(file, file->sampleframes);
+    if (file->sampleframes < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+MS_ADPCM_Init(WaveFile *file, size_t datalength)
+{
+    WaveFormat *format = &file->format;
+    WaveChunk *chunk = &file->chunk;
+    const size_t blockheadersize = (size_t)format->channels * 7;
+    const size_t blockdatasize = (size_t)format->blockalign - blockheadersize;
+    const size_t blockframebitsize = (size_t)format->bitspersample * format->channels;
+    const size_t blockdatasamples = (blockdatasize * 8) / blockframebitsize;
+    const Sint16 presetcoeffs[14] = {256, 0, 512, -256, 0, 0, 192, 64, 240, 0, 460, -208, 392, -232};
+    size_t i, coeffcount;
+    MS_ADPCM_CoeffData *coeffdata;
+
+    /* Sanity checks. */
+
+    /* While it's clear how IMA ADPCM handles more than two channels, the nibble
+     * order of MS ADPCM makes it awkward. The Standards Update does not talk
+     * about supporting more than stereo anyway.
+     */
+    if (format->channels > 2) {
+        return SDL_SetError("Invalid number of channels");
+    }
+
+    if (format->bitspersample != 4) {
+        return SDL_SetError("Invalid MS ADPCM bits per sample of %u", (unsigned int)format->bitspersample);
+    }
+
+    /* The block size must be big enough to contain the block header. */
+    if (format->blockalign < blockheadersize) {
+        return SDL_SetError("Invalid MS ADPCM block size (nBlockAlign)");
+    }
+
+    if (format->formattag == EXTENSIBLE_CODE) {
+        /* Does have a GUID (like all format tags), but there's no specification
+         * for how the data is packed into the extensible header. Making
+         * assumptions here could lead to new formats nobody wants to support.
+         */
+        return SDL_SetError("MS ADPCM with the extensible header is not supported");
+    }
+
+    /* There are wSamplesPerBlock, wNumCoef, and at least 7 coefficient pairs in
+     * the extended part of the header.
+     */
+    if (chunk->size < 22) {
+        return SDL_SetError("Could not read MS ADPCM format header");
+    }
+
+    format->samplesperblock = chunk->data[18] | ((Uint16)chunk->data[19] << 8);
+    /* Number of coefficient pairs. A pair has two 16-bit integers. */
+    coeffcount = chunk->data[20] | ((size_t)chunk->data[21] << 8);
+    /* bPredictor, the integer offset into the coefficients array, is only
+     * 8 bits. It can only address the first 256 coefficients. Let's limit
+     * the count number here.
+     */
+    if (coeffcount > 256) {
+        coeffcount = 256;
+    }
+
+    if (chunk->size < 22 + coeffcount * 4) {
+        return SDL_SetError("Could not read custom coefficients in MS ADPCM format header");
+    } else if (format->extsize < 4 + coeffcount * 4) {
+        return SDL_SetError("Invalid MS ADPCM format header (too small)");
+    } else if (coeffcount < 7) {
+        return SDL_SetError("Missing required coefficients in MS ADPCM format header");
+    }
+
+    coeffdata = (MS_ADPCM_CoeffData *)SDL_malloc(sizeof(MS_ADPCM_CoeffData) + coeffcount * 4);
+    file->decoderdata = coeffdata; /* Freed in cleanup. */
+    if (coeffdata == NULL) {
+        return SDL_OutOfMemory();
+    }
+    coeffdata->coeff = &coeffdata->aligndummy;
+    coeffdata->coeffcount = (Uint16)coeffcount;
+
+    /* Copy the 16-bit pairs. */
+    for (i = 0; i < coeffcount * 2; i++) {
+        Sint32 c = chunk->data[22 + i * 2] | ((Sint32)chunk->data[23 + i * 2] << 8);
+        if (c >= 0x8000) {
+            c -= 0x10000;
+        }
+        if (i < 14 && c != presetcoeffs[i]) {
+            return SDL_SetError("Wrong preset coefficients in MS ADPCM format header");
+        }
+        coeffdata->coeff[i] = (Sint16)c;
+    }
+
+    /* Technically, wSamplesPerBlock is required, but we have all the
+     * information in the other fields to calculate it, if it's zero.
+     */
+    if (format->samplesperblock == 0) {
+        /* Let's be nice to the encoders that didn't know how to fill this.
+         * The Standards Update calculates it this way:
+         *
+         *   x = Block size (in bits) minus header size (in bits)
+         *   y = Bit depth multiplied by channel count
+         *   z = Number of samples per channel in block header
+         *   wSamplesPerBlock = x / y + z
+         */
+        format->samplesperblock = (Uint32)blockdatasamples + 2;
+    }
+
+    /* nBlockAlign can be in conflict with wSamplesPerBlock. For example, if
+     * the number of samples doesn't fit into the block. The Standards Update
+     * also describes wSamplesPerBlock with a formula that makes it necessary to
+     * always fill the block with the maximum amount of samples, but this is not
+     * enforced here as there are no compatibility issues.
+     * A truncated block header with just one sample is not supported.
+     */
+    if (format->samplesperblock == 1 || blockdatasamples < format->samplesperblock - 2) {
+        return SDL_SetError("Invalid number of samples per MS ADPCM block (wSamplesPerBlock)");
+    }
+
+    if (MS_ADPCM_CalculateSampleFrames(file, datalength) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static Sint16
+MS_ADPCM_ProcessNibble(MS_ADPCM_ChannelState *cstate, Sint32 sample1, Sint32 sample2, Uint8 nybble)
+{
+    const Sint32 max_audioval = 32767;
+    const Sint32 min_audioval = -32768;
+    const Uint16 max_deltaval = 65535;
+    const Uint16 adaptive[] = {
+        230, 230, 230, 230, 307, 409, 512, 614,
+        768, 614, 512, 409, 307, 230, 230, 230
+    };
+    Sint32 new_sample;
+    Sint32 errordelta;
+    Uint32 delta = cstate->delta;
+
+    new_sample = (sample1 * cstate->coeff1 + sample2 * cstate->coeff2) / 256;
+    /* The nibble is a signed 4-bit error delta. */
+    errordelta = (Sint32)nybble - (nybble >= 0x08 ? 0x10 : 0);
+    new_sample += (Sint32)delta * errordelta;
+    if (new_sample < min_audioval) {
+        new_sample = min_audioval;
+    } else if (new_sample > max_audioval) {
+        new_sample = max_audioval;
+    }
+    delta = (delta * adaptive[nybble]) / 256;
+    if (delta < 16) {
+        delta = 16;
+    } else if (delta > max_deltaval) {
+        /* This issue is not described in the Standards Update and therefore
+         * undefined. It seems sensible to prevent overflows with a limit.
+         */
+        delta = max_deltaval;
+    }
+
+    cstate->delta = (Uint16)delta;
+    return (Sint16)new_sample;
+}
+
+static int
+MS_ADPCM_DecodeBlockHeader(ADPCM_DecoderState *state)
+{
+    Uint8 coeffindex;
+    const Uint32 channels = state->channels;
+    Sint32 sample;
+    Uint32 c;
+    MS_ADPCM_ChannelState *cstate = (MS_ADPCM_ChannelState *)state->cstate;
+    MS_ADPCM_CoeffData *ddata = (MS_ADPCM_CoeffData *)state->ddata;
+
+    for (c = 0; c < channels; c++) {
+        size_t o = c;
+
+        /* Load the coefficient pair into the channel state. */
+        coeffindex = state->block.data[o];
+        if (coeffindex > ddata->coeffcount) {
+            return SDL_SetError("Invalid MS ADPCM coefficient index in block header");
+        }
+        cstate[c].coeff1 = ddata->coeff[coeffindex * 2];
+        cstate[c].coeff2 = ddata->coeff[coeffindex * 2 + 1];
+
+        /* Initial delta value. */
+        o = channels + c * 2;
+        cstate[c].delta = state->block.data[o] | ((Uint16)state->block.data[o + 1] << 8);
+
+        /* Load the samples from the header. Interestingly, the sample later in
+         * the output stream comes first.
+         */
+        o = channels * 3 + c * 2;
+        sample = state->block.data[o] | ((Sint32)state->block.data[o + 1] << 8);
+        if (sample >= 0x8000) {
+            sample -= 0x10000;
+        }
+        state->output.data[state->output.pos + channels] = (Sint16)sample;
+
+        o = channels * 5 + c * 2;
+        sample = state->block.data[o] | ((Sint32)state->block.data[o + 1] << 8);
+        if (sample >= 0x8000) {
+            sample -= 0x10000;
+        }
+        state->output.data[state->output.pos] = (Sint16)sample;
+
+        state->output.pos++;
+    }
+
+    state->block.pos += state->blockheadersize;
+
+    /* Skip second sample frame that came from the header. */
+    state->output.pos += state->channels;
+
+    /* Header provided two sample frames. */
+    state->framesleft -= 2;
+
+    return 0;
+}
+
+/* Decodes the data of the MS ADPCM block. Decoding will stop if a block is too
+ * short, returning with none or partially decoded data. The partial data
+ * will always contain full sample frames (same sample count for each channel).
+ * Incomplete sample frames are discarded.
+ */
+static int
+MS_ADPCM_DecodeBlockData(ADPCM_DecoderState *state)
+{
+    Uint16 nybble = 0;
+    Sint16 sample1, sample2;
+    const Uint32 channels = state->channels;
+    Uint32 c;
+    MS_ADPCM_ChannelState *cstate = (MS_ADPCM_ChannelState *)state->cstate;
+
+    size_t blockpos = state->block.pos;
+    size_t blocksize = state->block.size;
+
+    size_t outpos = state->output.pos;
+
+    Sint64 blockframesleft = state->samplesperblock - 2;
+    if (blockframesleft > state->framesleft) {
+        blockframesleft = state->framesleft;
+    }
+
+    while (blockframesleft > 0) {
+        for (c = 0; c < channels; c++) {
+            if (nybble & 0x4000) {
+                nybble <<= 4;
+            } else if (blockpos < blocksize) {
+                nybble = state->block.data[blockpos++] | 0x4000;
+            } else {
+                /* Out of input data. Drop the incomplete frame and return. */
+                state->output.pos = outpos - c;
+                return -1;
+            }
+
+            /* Load previous samples which may come from the block header. */
+            sample1 = state->output.data[outpos - channels];
+            sample2 = state->output.data[outpos - channels * 2];
+
+            sample1 = MS_ADPCM_ProcessNibble(cstate + c, sample1, sample2, (nybble >> 4) & 0x0f);
+            state->output.data[outpos++] = sample1;
+        }
+
+        state->framesleft--;
+        blockframesleft--;
+    }
+
+    state->output.pos = outpos;
+
+    return 0;
+}
+
+static int
+MS_ADPCM_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
+{
+    int result;
+    size_t bytesleft, outputsize;
+    WaveChunk *chunk = &file->chunk;
+    ADPCM_DecoderState state;
+    MS_ADPCM_ChannelState cstate[2];
+
+    SDL_zero(state);
+    SDL_zero(cstate);
+
+    if (chunk->size != chunk->length) {
+        /* Could not read everything. Recalculate number of sample frames. */
+        if (MS_ADPCM_CalculateSampleFrames(file, chunk->size) < 0) {
+            return -1;
+        }
+    }
+
+    /* Nothing to decode, nothing to return. */
+    if (file->sampleframes == 0) {
+        *audio_buf = NULL;
+        *audio_len = 0;
+        return 0;
+    }
+
+    state.blocksize = file->format.blockalign;
+    state.channels = file->format.channels;
+    state.blockheadersize = (size_t)state.channels * 7;
+    state.samplesperblock = file->format.samplesperblock;
+    state.framesize = state.channels * sizeof(Sint16);
+    state.ddata = file->decoderdata;
+    state.framestotal = file->sampleframes;
+    state.framesleft = state.framestotal;
+
+    state.input.data = chunk->data;
+    state.input.size = chunk->size;
+    state.input.pos = 0;
+
+    /* The output size in bytes. May get modified if data is truncated. */
+    outputsize = (size_t)state.framestotal;
+    if (SafeMult(&outputsize, state.framesize)) {
+        return SDL_OutOfMemory();
+    } else if (outputsize > SDL_MAX_UINT32 || state.framestotal > SIZE_MAX) {
+        return SDL_SetError("WAVE file too big");
+    }
+
+    state.output.pos = 0;
+    state.output.size = outputsize / sizeof(Sint16);
+    state.output.data = (Sint16 *)SDL_malloc(outputsize);
+    if (state.output.data == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    state.cstate = &cstate;
+
+    /* Decode block by block. A truncated block will stop the decoding. */
+    bytesleft = state.input.size - state.input.pos;
+    while (state.framesleft > 0 && bytesleft >= state.blockheadersize) {
+        state.block.data = state.input.data + state.input.pos;
+        state.block.size = bytesleft < state.blocksize ? bytesleft : state.blocksize;
+        state.block.pos = 0;
+
+        if (state.output.size - state.output.pos < (Uint64)state.framesleft * state.channels) {
+            /* Somehow didn't allocate enough space for the output. */
+            SDL_free(state.output.data);
+            return SDL_SetError("Unexpected overflow in MS ADPCM decoder");
+        }
+
+        /* Initialize decoder with the values from the block header. */
+        result = MS_ADPCM_DecodeBlockHeader(&state);
+        if (result == -1) {
+            SDL_free(state.output.data);
+            return -1;
+        }
+
+        /* Decode the block data. It stores the samples directly in the output. */
+        result = MS_ADPCM_DecodeBlockData(&state);
+        if (result == -1) {
+            /* Unexpected end. Stop decoding and return partial data if necessary. */
+            if (file->trunchint == TruncVeryStrict || file->trunchint == TruncVeryStrict) {
+                SDL_free(state.output.data);
+                return SDL_SetError("Truncated data chunk");
+            } else if (file->trunchint != TruncDropFrame) {
+                state.output.pos -= state.output.pos % (state.samplesperblock * state.channels);
+            }
+            outputsize = state.output.pos * sizeof(Sint16); /* Can't overflow, is always smaller. */
+            break;
+        }
+
+        state.input.pos += state.block.size;
+        bytesleft = state.input.size - state.input.pos;
+    }
+
+    *audio_buf = (Uint8 *)state.output.data;
+    *audio_len = (Uint32)outputsize;
+
+    return 0;
+}
+
+static int
+IMA_ADPCM_CalculateSampleFrames(WaveFile *file, size_t datalength)
+{
+    WaveFormat *format = &file->format;
+    const size_t blockheadersize = (size_t)format->channels * 4;
+    const size_t subblockframesize = (size_t)format->channels * 4;
+    const size_t availableblocks = datalength / format->blockalign;
+    const size_t trailingdata = datalength % format->blockalign;
+
+    if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) {
+        /* The size of the data chunk must be a multiple of the block size. */
+        if (datalength < blockheadersize || trailingdata > 0) {
+            return SDL_SetError("Truncated IMA ADPCM block");
+        }
+    }
+
+    /* Calculate number of sample frames that will be decoded. */
+    file->sampleframes = (Uint64)availableblocks * format->samplesperblock;
+    if (trailingdata > 0) {
+        /* The last block is truncated. Check if we can get any samples out of it. */
+        if (file->trunchint == TruncDropFrame && trailingdata > blockheadersize - 2) {
+            /* The sample frame in the header of the truncated block is present.
+             * Drop incomplete sample frames.
+             */
+            size_t trailingsamples = 1;
+
+            if (trailingdata > blockheadersize) {
+                /* More data following after the header. */
+                const size_t trailingblockdata = trailingdata - blockheadersize;
+                const size_t trailingsubblockdata = trailingblockdata % subblockframesize;
+                trailingsamples += (trailingblockdata / subblockframesize) * 8;
+                /* Due to the interleaved sub-blocks, the last 4 bytes determine
+                 * how many samples of the truncated sub-block are lost.
+                 */
+                if (trailingsubblockdata > subblockframesize - 4) {
+                    trailingsamples += (trailingsubblockdata % 4) * 2;
+                }
+            }
+
+            if (trailingsamples > format->samplesperblock) {
+                trailingsamples = format->samplesperblock;
+            }
+            file->sampleframes += trailingsamples;
+        }
+    }
+
+    file->sampleframes = WaveAdjustToFactValue(file, file->sampleframes);
+    if (file->sampleframes < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+IMA_ADPCM_Init(WaveFile *file, size_t datalength)
+{
+    WaveFormat *format = &file->format;
+    WaveChunk *chunk = &file->chunk;
+    const size_t blockheadersize = (size_t)format->channels * 4;
+    const size_t blockdatasize = (size_t)format->blockalign - blockheadersize;
+    const size_t blockframebitsize = (size_t)format->bitspersample * format->channels;
+    const size_t blockdatasamples = (blockdatasize * 8) / blockframebitsize;
+
+    /* Sanity checks. */
+
+    /* IMA ADPCM can also have 3-bit samples, but it's not supported by SDL at this time. */
+    if (format->bitspersample == 3) {
+        return SDL_SetError("3-bit IMA ADPCM currently not supported");
+    } else if (format->bitspersample != 4) {
+        return SDL_SetError("Invalid IMA ADPCM bits per sample of %u", (unsigned int)format->bitspersample);
+    }
+
+    /* The block size is required to be a multiple of 4 and it must be able to
+     * hold a block header.
+     */
+    if (format->blockalign < blockheadersize || format->blockalign % 4) {
+        return SDL_SetError("Invalid IMA ADPCM block size (nBlockAlign)");
+    }
+
+    if (format->formattag == EXTENSIBLE_CODE) {
+        /* There's no specification for this, but it's basically the same
+         * format because the extensible header has wSampePerBlocks too.
+         */
+    } else  {
+        /* The Standards Update says there 'should' be 2 bytes for wSamplesPerBlock. */
+        if (chunk->size >= 20 && format->extsize >= 2) {
+            format->samplesperblock = chunk->data[18] | ((Uint16)chunk->data[19] << 8);
+        }
+    }
+
+    if (format->samplesperblock == 0) {
+        /* Field zero? No problem. We just assume the encoder packed the block.
+         * The specification calculates it this way:
+         *
+         *   x = Block size (in bits) minus header size (in bits)
+         *   y = Bit depth multiplied by channel count
+         *   z = Number of samples per channel in header
+         *   wSamplesPerBlock = x / y + z
+         */
+        format->samplesperblock = (Uint32)blockdatasamples + 1;
+    }
+
+    /* nBlockAlign can be in conflict with wSamplesPerBlock. For example, if
+     * the number of samples doesn't fit into the block. The Standards Update
+     * also describes wSamplesPerBlock with a formula that makes it necessary
+     * to always fill the block with the maximum amount of samples, but this is
+     * not enforced here as there are no compatibility issues.
+     */
+    if (blockdatasamples < format->samplesperblock - 1) {
+        return SDL_SetError("Invalid number of samples per IMA ADPCM block (wSamplesPerBlock)");
+    }
+
+    if (IMA_ADPCM_CalculateSampleFrames(file, datalength) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static Sint16
+IMA_ADPCM_ProcessNibble(Sint8 *cindex, Sint16 lastsample, Uint8 nybble)
+{
+    const Sint32 max_audioval = 32767;
+    const Sint32 min_audioval = -32768;
+    const Sint8 index_table_4b[16] = {
+        -1, -1, -1, -1,
+        2, 4, 6, 8,
+        -1, -1, -1, -1,
+        2, 4, 6, 8
+    };
+    const Uint16 step_table[89] = {
+        7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31,
+        34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130,
+        143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408,
+        449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282,
+        1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
+        3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630,
+        9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350,
+        22385, 24623, 27086, 29794, 32767
+    };
+    Uint32 step;
+    Sint32 sample, delta;
+    Sint8 index = *cindex;
+
+    /* Clamp index into valid range. */
+    if (index > 88) {
+        index = 88;
+    } else if (index < 0) {
+        index = 0;
+    }
+
+    /* explicit cast to avoid gcc warning about using 'char' as array index */
+    step = step_table[(size_t)index];
+
+    /* Update index value */
+    *cindex = index + index_table_4b[nybble];
+
+    /* This calculation uses shifts and additions because multiplications were
+     * much slower back then. Sadly, this can't just be replaced with an actual
+     * multiplication now as the old algorithm drops some bits. The closest
+     * approximation I could find is something like this:
+     * (nybble & 0x8 ? -1 : 1) * ((nybble & 0x7) * step / 4 + step / 8)
+     */
+    delta = step >> 3;
+    if (nybble & 0x04)
+        delta += step;
+    if (nybble & 0x02)
+        delta += step >> 1;
+    if (nybble & 0x01)
+        delta += step >> 2;
+    if (nybble & 0x08)
+        delta = -delta;
+
+    sample = lastsample + delta;
+
+    /* Clamp output sample */
+    if (sample > max_audioval) {
+        sample = max_audioval;
+    } else if (sample < min_audioval) {
+        sample = min_audioval;
+    }
+
+    return (Sint16)sample;
+}
+
+static int
+IMA_ADPCM_DecodeBlockHeader(ADPCM_DecoderState *state)
+{
+    Sint16 step;
+    Uint32 c;
+    Uint8 *cstate = state->cstate;
+
+    for (c = 0; c < state->channels; c++) {
+        size_t o = state->block.pos + c * 4;
+
+        /* Extract the sample from the header. */
+        Sint32 sample = state->block.data[o] | ((Sint32)state->block.data[o + 1] << 8);
+        if (sample >= 0x8000) {
+            sample -= 0x10000;
+        }
+        state->output.data[state->output.pos++] = (Sint16)sample;
+
+        /* Channel step index. */
+        step = (Sint16)state->block.data[o + 2];
+        cstate[c] = (Sint8)(step > 0x80 ? step - 0x100 : step);
+
+        /* Reserved byte in block header, should be 0. */
+        if (state->block.data[o + 3] != 0) {
+            /* Uh oh, corrupt data?  Buggy code? */ ;
+        }
+    }
+
+    state->block.pos += state->blockheadersize;
+
+    /* Header provided one sample frame. */
+    state->framesleft--;
+
+    return 0;
+}
+
+/* Decodes the data of the IMA ADPCM block. Decoding will stop if a block is too
+ * short, returning with none or partially decoded data. The partial data always
+ * contains full sample frames (same sample count for each channel).
+ * Incomplete sample frames are discarded.
+ */
+static int
+IMA_ADPCM_DecodeBlockData(ADPCM_DecoderState *state)
+{
+    size_t i;
+    int retval = 0;
+    const Uint32 channels = state->channels;
+    const size_t subblockframesize = channels * 4;
+    Uint64 bytesrequired;
+    Uint32 c;
+
+    size_t blockpos = state->block.pos;
+    size_t blocksize = state->block.size;
+    size_t blockleft = blocksize - blockpos;
+
+    size_t outpos = state->output.pos;
+
+    Sint64 blockframesleft = state->samplesperblock - 1;
+    if (blockframesleft > state->framesleft) {
+        blockframesleft = state->framesleft;
+    }
+
+    bytesrequired = (blockframesleft + 7) / 8 * subblockframesize;
+    if (blockleft < bytesrequired) {
+        /* Data truncated. Calculate how many samples we can get out if it. */
+        const size_t guaranteedframes = blockleft / subblockframesize;
+        const size_t remainingbytes = blockleft % subblockframesize;
+        blockframesleft = guaranteedframes;
+        if (remainingbytes > subblockframesize - 4) {
+            blockframesleft += (remainingbytes % 4) * 2;
+        }
+        /* Signal the truncation. */
+        retval = -1;
+    }
+
+    /* Each channel has their nibbles packed into 32-bit blocks. These blocks
+     * are interleaved and make up the data part of the ADPCM block. This loop
+     * decodes the samples as they come from the input data and puts them at
+     * the appropriate places in the output data.
+     */
+    while (blockframesleft > 0) {
+        const size_t subblocksamples = blockframesleft < 8 ? (size_t)blockframesleft : 8;
+
+        for (c = 0; c < channels; c++) {
+            Uint8 nybble = 0;
+            /* Load previous sample which may come from the block header. */
+            Sint16 sample = state->output.data[outpos + c - channels];
+
+            for (i = 0; i < subblocksamples; i++) {
+                if (i & 1) {
+                    nybble >>= 4;
+                } else {
+                    nybble = state->block.data[blockpos++];
+                }
+
+                sample = IMA_ADPCM_ProcessNibble((Sint8 *)state->cstate + c, sample, nybble & 0x0f);
+                state->output.data[outpos + c + i * channels] = sample;
+            }
+        }
+
+        outpos += channels * subblocksamples;
+        state->framesleft -= subblocksamples;
+        blockframesleft -= subblocksamples;
+    }
+
+    state->block.pos = blockpos;
+    state->output.pos = outpos;
+
+    return retval;
+}
+
+static int
+IMA_ADPCM_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
+{
+    int result;
+    size_t bytesleft, outputsize;
+    WaveChunk *chunk = &file->chunk;
+    ADPCM_DecoderState state;
+    Sint8 *cstate;
+
+    if (chunk->size != chunk->length) {
+        /* Could not read everything. Recalculate number of sample frames. */
+        if (IMA_ADPCM_CalculateSampleFrames(file, chunk->size) < 0) {
+            return -1;
+        }
+    }
+
+    /* Nothing to decode, nothing to return. */
+    if (file->sampleframes == 0) {
+        *audio_buf = NULL;
+        *audio_len = 0;
+        return 0;
+    }
+
+    SDL_zero(state);
+    state.channels = file->format.channels;
+    state.blocksize = file->format.blockalign;
+    state.blockheadersize = (size_t)state.channels * 4;
+    state.samplesperblock = file->format.samplesperblock;
+    state.framesize = state.channels * sizeof(Sint16);
+    state.framestotal = file->sampleframes;
+    state.framesleft = state.framestotal;
+
+    state.input.data = chunk->data;
+    state.input.size = chunk->size;
+    state.input.pos = 0;
+
+    /* The output size in bytes. May get modified if data is truncated. */
+    outputsize = (size_t)state.framestotal;
+    if (SafeMult(&outputsize, state.framesize)) {
+        return SDL_OutOfMemory();
+    } else if (outputsize > SDL_MAX_UINT32 || state.framestotal > SIZE_MAX) {
+        return SDL_SetError("WAVE file too big");
+    }
+
+    state.output.pos = 0;
+    state.output.size = outputsize / sizeof(Sint16);
+    state.output.data = (Sint16 *)SDL_malloc(outputsize);
+    if (state.output.data == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    cstate = (Sint8 *)SDL_calloc(state.channels, sizeof(Sint8));
+    if (cstate == NULL) {
+        SDL_free(state.output.data);
+        return SDL_OutOfMemory();
+    }
+    state.cstate = cstate;
+
+    /* Decode block by block. A truncated block will stop the decoding. */
+    bytesleft = state.input.size - state.input.pos;
+    while (state.framesleft > 0 && bytesleft >= state.blockheadersize) {
+        state.block.data = state.input.data + state.input.pos;
+        state.block.size = bytesleft < state.blocksize ? bytesleft : state.blocksize;
+        state.block.pos = 0;
+
+        if (state.output.size - state.output.pos < (Uint64)state.framesleft * state.channels) {
+            /* Somehow didn't allocate enough space for the output. */
+            SDL_free(state.output.data);
+            SDL_free(cstate);
+            return SDL_SetError("Unexpected overflow in IMA ADPCM decoder");
+        }
+
+        /* Initialize decoder with the values from the block header. */
+        result = IMA_ADPCM_DecodeBlockHeader(&state);
+        if (result == 0) {
+            /* Decode the block data. It stores the samples directly in the output. */
+            result = IMA_ADPCM_DecodeBlockData(&state);
+        }
+
+        if (result == -1) {
+            /* Unexpected end. Stop decoding and return partial data if necessary. */
+            if (file->trunchint == TruncVeryStrict || file->trunchint == TruncVeryStrict) {
+                SDL_free(state.output.data);
+                SDL_free(cstate);
+                return SDL_SetError("Truncated data chunk");
+            } else if (file->trunchint != TruncDropFrame) {
+                state.output.pos -= state.output.pos % (state.samplesperblock * state.channels);
+            }
+            outputsize = state.output.pos * sizeof(Sint16); /* Can't overflow, is always smaller. */
+            break;
+        }
+
+        state.input.pos += state.block.size;
+        bytesleft = state.input.size - state.input.pos;
+    }
+
+    *audio_buf = (Uint8 *)state.output.data;
+    *audio_len = (Uint32)outputsize;
+
+    SDL_free(cstate);
+
+    return 0;
+}
+
+static int
+LAW_Init(WaveFile *file, size_t datalength)
+{
+    WaveFormat *format = &file->format;
+
+    /* Standards Update requires this to be 8. */
+    if (format->bitspersample != 8) {
+        return SDL_SetError("Invalid companded bits per sample of %u", (unsigned int)format->bitspersample);
+    }
+
+    /* Not going to bother with weird padding. */
+    if (format->blockalign != format->channels) {
+        return SDL_SetError("Unsupported block alignment");
+    }
+
+    if ((file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict)) {
+        if (format->blockalign > 1 && datalength % format->blockalign) {
+            return SDL_SetError("Truncated data chunk in WAVE file");
+        }
+    }
+
+    file->sampleframes = WaveAdjustToFactValue(file, datalength / format->blockalign);
+    if (file->sampleframes < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+LAW_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
+{
+#ifdef SDL_WAVE_LAW_LUT
+    const Sint16 alaw_lut[256] = {
+        -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752,
+        -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, -22016,
+        -20992, -24064, -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, -11008,
+        -10496, -12032, -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, -344,
+        -328, -376, -360, -280, -264, -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88,
+        -72, -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, -184, -168, -1376,
+        -1312, -1504, -1440, -1120, -1056, -1248, -1184, -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688,
+        -656, -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, -784, -880, -848, 5504,
+        5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752,
+        2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016,
+        20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008,
+        10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344,
+        328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88,
+        72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376,
+        1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688,
+        656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848
+    };
+    const Sint16 mulaw_lut[256] = {
+        -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, -15996,
+        -15484, -14972, -14460, -13948, -13436, -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, -7932,
+        -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900,
+        -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, -1884,
+        -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876,
+        -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, -492, -460, -428, -396, -372,
+        -356, -340, -324, -308, -292, -276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120,
+        -112, -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0, 32124,
+        31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996,
+        15484, 14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 7932,
+        7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900,
+        3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 1884,
+        1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 876,
+        844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460, 428, 396, 372,
+        356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120,
+        112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0
+    };
+#endif
+
+    WaveFormat *format = &file->format;
+    WaveChunk *chunk = &file->chunk;
+    size_t i, sample_count, expanded_len;
+    Uint8 *src;
+    Sint16 *dst;
+
+    if (chunk->length != chunk->size) {
+        file->sampleframes = WaveAdjustToFactValue(file, chunk->size / format->blockalign);
+        if (file->sampleframes < 0) {
+            return -1;
+        }
+    }
+
+    /* Nothing to decode, nothing to return. */
+    if (file->sampleframes == 0) {
+        *audio_buf = NULL;
+        *audio_len = 0;
+        return 0;
+    }
+
+    sample_count = (size_t)file->sampleframes;
+    if (SafeMult(&sample_count, format->channels)) {
+        return SDL_OutOfMemory();
+    }
+
+    expanded_len = sample_count;
+    if (SafeMult(&expanded_len, sizeof(Sint16))) {
+        return SDL_OutOfMemory();
+    } else if (expanded_len > SDL_MAX_UINT32 || file->sampleframes > SIZE_MAX) {
+        return SDL_SetError("WAVE file too big");
+    }
+
+    /* 1 to avoid allocating zero bytes, to keep static analysis happy. */
+    src = (Uint8 *)SDL_realloc(chunk->data, expanded_len ? expanded_len : 1);
+    if (src == NULL) {
+        return SDL_OutOfMemory();
+    }
+    chunk->data = NULL;
+    chunk->size = 0;
+
+    dst = (Sint16 *)src;
+
+    /* Work backwards, since we're expanding in-place. SDL_AudioSpec.format will
+     * inform the caller about the byte order.
+     */
+    i = sample_count;
+    switch (file->format.encoding) {
+#ifdef SDL_WAVE_LAW_LUT
+    case ALAW_CODE:
+        while (i--) {
+            dst[i] = alaw_lut[src[i]];
+        }
+        break;
+    case MULAW_CODE:
+        while (i--) {
+            dst[i] = mulaw_lut[src[i]];
+        }
+        break;
+#else
+    case ALAW_CODE:
+        while (i--) {
+            Uint8 nibble = src[i];
+            Uint8 exponent = (nibble & 0x7f) ^ 0x55;
+            Sint16 mantissa = exponent & 0xf;
+
+            exponent >>= 4;
+            if (exponent > 0) {
+                mantissa |= 0x10;
+            }
+            mantissa = (mantissa << 4) | 0x8;
+            if (exponent > 1) {
+                mantissa <<= exponent - 1;
+            }
+
+            dst[i] = nibble & 0x80 ? mantissa : -mantissa;
+        }
+        break;
+    case MULAW_CODE:
+        while (i--) {
+            Uint8 nibble = ~src[i];
+            Sint16 mantissa = nibble & 0xf;
+            Uint8 exponent = (nibble >> 4) & 0x7;
+            Sint16 step = 4 << (exponent + 1);
+
+            mantissa = (0x80 << exponent) + step * mantissa + step / 2 - 132;
+
+            dst[i] = nibble & 0x80 ? -mantissa : mantissa;
+        }
+        break;
+#endif
+    default:
+        SDL_free(src);
+        return SDL_SetError("Unknown companded encoding");
+    }
+
+    *audio_buf = src;
+    *audio_len = (Uint32)expanded_len;
+
+    return 0;
+}
+
+static int
+PCM_Init(WaveFile *file, size_t datalength)
+{
+    WaveFormat *format = &file->format;
+
+    if (format->encoding == PCM_CODE) {
+        switch (format->bitspersample) {
+        case 8:
+        case 16:
+        case 24:
+        case 32:
+            /* These are supported. */
+            break;
+        default:
+            return SDL_SetError("%u-bit PCM format not supported", (unsigned int)format->bitspersample);
+        }
+    } else if (format->encoding == IEEE_FLOAT_CODE) {
+        if (format->bitspersample != 32) {
+            return SDL_SetError("%u-bit IEEE floating-point format not supported", (unsigned int)format->bitspersample);
+        }
+    }
+
+    /* It wouldn't be that hard to support more exotic block sizes, but
+     * the most common formats should do for now.
+     */
+    if (format->blockalign * 8 != format->channels * format->bitspersample) {
+        return SDL_SetError("Unsupported block alignment");
+    }
+
+    if ((file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict)) {
+        if (format->blockalign > 1 && datalength % format->blockalign) {
+            return SDL_SetError("Truncated data chunk in WAVE file");
+        }
+    }
+
+    file->sampleframes = WaveAdjustToFactValue(file, datalength / format->blockalign);
+    if (file->sampleframes < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+PCM_ConvertSint24ToSint32(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
+{
+    WaveFormat *format = &file->format;
+    WaveChunk *chunk = &file->chunk;
+    size_t i, expanded_len, sample_count;
+    Uint8 *ptr;
+
+    sample_count = (size_t)file->sampleframes;
+    if (SafeMult(&sample_count, format->channels)) {
+        return SDL_OutOfMemory();
+    }
+
+    expanded_len = sample_count;
+    if (SafeMult(&expanded_len, sizeof(Sint32))) {
+        return SDL_OutOfMemory();
+    } else if (expanded_len > SDL_MAX_UINT32 || file->sampleframes > SIZE_MAX) {
+        return SDL_SetError("WAVE file too big");
+    }
+
+    /* 1 to avoid allocating zero bytes, to keep static analysis happy. */
+    ptr = (Uint8 *)SDL_realloc(chunk->data, expanded_len ? expanded_len : 1);
+    if (ptr == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    /* This pointer is now invalid. */
+    chunk->data = NULL;
+    chunk->size = 0;
+
+    *audio_buf = ptr;
+    *audio_len = (Uint32)expanded_len;
+
+    /* work from end to start, since we're expanding in-place. */
+    for (i = sample_count; i > 0; i--) {
+        const size_t o = i - 1;
+        uint8_t b[4];
+
+        b[0] = 0;
+        b[1] = ptr[o * 3];
+        b[2] = ptr[o * 3 + 1];
+        b[3] = ptr[o * 3 + 2];
+
+        ptr[o * 4 + 0] = b[0];
+        ptr[o * 4 + 1] = b[1];
+        ptr[o * 4 + 2] = b[2];
+        ptr[o * 4 + 3] = b[3];
+    }
+
+    return 0;
+}
+
+static int
+PCM_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len)
+{
+    WaveFormat *format = &file->format;
+    WaveChunk *chunk = &file->chunk;
+    size_t outputsize;
+
+    if (chunk->length != chunk->size) {
+        file->sampleframes = WaveAdjustToFactValue(file, chunk->size / format->blockalign);
+        if (file->sampleframes < 0) {
+            return -1;
+        }
+    }
+
+    /* Nothing to decode, nothing to return. */
+    if (file->sampleframes == 0) {
+        *audio_buf = NULL;
+        *audio_len = 0;
+        return 0;
+    }
+
+    /* 24-bit samples get shifted to 32 bits. */
+    if (format->encoding == PCM_CODE && format->bitspersample == 24) {
+        return PCM_ConvertSint24ToSint32(file, audio_buf, audio_len);
+    }
+
+    outputsize = (size_t)file->sampleframes;
+    if (SafeMult(&outputsize, format->blockalign)) {
+        return SDL_OutOfMemory();
+    } else if (outputsize > SDL_MAX_UINT32 || file->sampleframes > SIZE_MAX) {
+        return SDL_SetError("WAVE file too big");
+    }
+
+    *audio_buf = chunk->data;
+    *audio_len = (Uint32)outputsize;
+
+    /* This pointer is going to be returned to the caller. Prevent free in cleanup. */
+    chunk->data = NULL;
+    chunk->size = 0;
+
+    return 0;
+}
+
+static WaveRiffSizeHint
+WaveGetRiffSizeHint()
+{
+    const char *hint = SDL_GetHint(SDL_HINT_WAVE_RIFF_CHUNK_SIZE);
+
+    if (hint != NULL) {
+        if (SDL_strcmp(hint, "force") == 0) {
+            return RiffSizeForce;
+        } else if (SDL_strcmp(hint, "ignore") == 0) {
+            return RiffSizeIgnore;
+        } else if (SDL_strcmp(hint, "ignorezero") == 0) {
+            return RiffSizeIgnoreZero;
+        } else if (SDL_strcmp(hint, "maximum") == 0) {
+            return RiffSizeMaximum;
+        }
+    }
 
+    return RiffSizeNoHint;
+}
+
+static WaveTruncationHint
+WaveGetTruncationHint()
+{
+    const char *hint = SDL_GetHint(SDL_HINT_WAVE_TRUNCATION);
+
+    if (hint != NULL) {
+        if (SDL_strcmp(hint, "verystrict") == 0) {
+            return TruncVeryStrict;
+        } else if (SDL_strcmp(hint, "strict") == 0) {
+            return TruncStrict;
+        } else if (SDL_strcmp(hint, "dropframe") == 0) {
+            return TruncDropFrame;
+        } else if (SDL_strcmp(hint, "dropblock") == 0) {
+            return TruncDropBlock;
+        }
+    }
 
-static int ReadChunk(SDL_RWops * src, Chunk * chunk);
+    return TruncNoHint;
+}
 
-struct MS_ADPCM_decodestate
+static WaveFactChunkHint
+WaveGetFactChunkHint()
 {
-    Uint8 hPredictor;
-    Uint16 iDelta;
-    Sint16 iSamp1;
-    Sint16 iSamp2;
-};
-static struct MS_ADPCM_decoder
+    const char *hint = SDL_GetHint(SDL_HINT_WAVE_FACT_CHUNK);
+
+    if (hint != NULL) {
+        if (SDL_strcmp(hint, "truncate") == 0) {
+            return FactTruncate;
+        } else if (SDL_strcmp(hint, "strict") == 0) {
+            return FactStrict;
+        } else if (SDL_strcmp(hint, "ignorezero") == 0) {
+            return FactIgnoreZero;
+        } else if (SDL_strcmp(hint, "ignore") == 0) {
+            return FactIgnore;
+        }
+    }
+
+    return FactNoHint;
+}
+
+static void
+WaveFreeChunkData(WaveChunk *chunk)
 {
-    WaveFMT wavefmt;
-    Uint16 wSamplesPerBlock;
-    Uint16 wNumCoef;
-    Sint16 aCoeff[7][2];
-    /* * * */
-    struct MS_ADPCM_decodestate state[2];
-} MS_ADPCM_state;
+    if (chunk->data != NULL) {
+        SDL_free(chunk->data);
+        chunk->data = NULL;
+    }
+    chunk->size = 0;
+}
 
 static int
-InitMS_ADPCM(WaveFMT * format)
-{
-    Uint8 *rogue_feel;
-    int i;
-
-    /* Set the rogue pointer to the MS_ADPCM specific data */
-    MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding);
-    MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels);
-    MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency);
-    MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate);
-    MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign);
-    MS_ADPCM_state.wavefmt.bitspersample =
-        SDL_SwapLE16(format->bitspersample);
-    rogue_feel = (Uint8 *) format + sizeof(*format);
-    if (sizeof(*format) == 16) {
-        /* const Uint16 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); */
-        rogue_feel += sizeof(Uint16);
-    }
-    MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1] << 8) | rogue_feel[0]);
-    rogue_feel += sizeof(Uint16);
-    MS_ADPCM_state.wNumCoef = ((rogue_feel[1] << 8) | rogue_feel[0]);
-    rogue_feel += sizeof(Uint16);
-    if (MS_ADPCM_state.wNumCoef != 7) {
-        SDL_SetError("Unknown set of MS_ADPCM coefficients");
-        return (-1);
-    }
-    for (i = 0; i < MS_ADPCM_state.wNumCoef; ++i) {
-        MS_ADPCM_state.aCoeff[i][0] = ((rogue_feel[1] << 8) | rogue_feel[0]);
-        rogue_feel += sizeof(Uint16);
-        MS_ADPCM_state.aCoeff[i][1] = ((rogue_feel[1] << 8) | rogue_feel[0]);
-        rogue_feel += sizeof(Uint16);
-    }
-    return (0);
-}
-
-static Sint32
-MS_ADPCM_nibble(struct MS_ADPCM_decodestate *state,
-                Uint8 nybble, Sint16 * coeff)
-{
-    const Sint32 max_audioval = ((1 << (16 - 1)) - 1);
-    const Sint32 min_audioval = -(1 << (16 - 1));
-    const Sint32 adaptive[] = {
-        230, 230, 230, 230, 307, 409, 512, 614,
-        768, 614, 512, 409, 307, 230, 230, 230
-    };
-    Sint32 new_sample, delta;
+WaveNextChunk(SDL_RWops *src, WaveChunk *chunk)
+{
+    Uint32 chunkheader[2];
+    Sint64 nextposition = chunk->position + chunk->length;
 
-    new_sample = ((state->iSamp1 * coeff[0]) +
-                  (state->iSamp2 * coeff[1])) / 256;
-    if (nybble & 0x08) {
-        new_sample += state->iDelta * (nybble - 0x10);
-    } else {
-        new_sample += state->iDelta * nybble;
+    /* Data is no longer valid after this function returns. */
+    WaveFreeChunkData(chunk);
+
+    /* Error on overflows. */
+    if (SDL_MAX_SINT64 - chunk->length < chunk->position || SDL_MAX_SINT64 - 8 < nextposition) {
+        return -1;
     }
-    if (new_sample < min_audioval) {
-        new_sample = min_audioval;
-    } else if (new_sample > max_audioval) {
-        new_sample = max_audioval;
+
+    /* RIFF chunks have a 2-byte alignment. Skip padding byte. */
+    if (chunk->length & 1) {
+        nextposition++;
     }
-    delta = ((Sint32) state->iDelta * adaptive[nybble]) / 256;
-    if (delta < 16) {
-        delta = 16;
+
+    if (SDL_RWseek(src, nextposition, RW_SEEK_SET) != nextposition) {
+        /* Not sure how we ended up here. Just abort. */
+        return -2;
+    } else if (SDL_RWread(src, chunkheader, 4, 2) != 2) {
+        return -1;
     }
-    state->iDelta = (Uint16) delta;
-    state->iSamp2 = state->iSamp1;
-    state->iSamp1 = (Sint16) new_sample;
-    return (new_sample);
+
+    chunk->fourcc = SDL_SwapLE32(chunkheader[0]);
+    chunk->length = SDL_SwapLE32(chunkheader[1]);
+    chunk->position = nextposition + 8;
+
+    return 0;
 }
 
 static int
-MS_ADPCM_decode(Uint8 ** audio_buf, Uint32 * audio_len)
-{
-    struct MS_ADPCM_decodestate *state[2];
-    Uint8 *freeable, *encoded, *decoded;
-    Sint32 encoded_len, samplesleft;
-    Sint8 nybble;
-    Uint8 stereo;
-    Sint16 *coeff[2];
-    Sint32 new_sample;
+WaveReadPartialChunkData(SDL_RWops *src, WaveChunk *chunk, size_t length)
+{
+    WaveFreeChunkData(chunk);
 
-    /* Allocate the proper sized output buffer */
-    encoded_len = *audio_len;
-    encoded = *audio_buf;
-    freeable = *audio_buf;
-    *audio_len = (encoded_len / MS_ADPCM_state.wavefmt.blockalign) *
-        MS_ADPCM_state.wSamplesPerBlock *
-        MS_ADPCM_state.wavefmt.channels * sizeof(Sint16);
-    *audio_buf = (Uint8 *) SDL_malloc(*audio_len);
-    if (*audio_buf == NULL) {
-        return SDL_OutOfMemory();
+    if (length > chunk->length) {
+        length = chunk->length;
+    }
+
+    if (length > 0) {
+        chunk->data = SDL_malloc(length);
+        if (chunk->data == NULL) {
+            return SDL_OutOfMemory();
+        }
+
+        if (SDL_RWseek(src, chunk->position, RW_SEEK_SET) != chunk->position) {
+            /* Not sure how we ended up here. Just abort. */
+            return -2;
+        }
+
+        chunk->size = SDL_RWread(src, chunk->data, 1, length);
+        if (chunk->size != length) {
+            /* Expected to be handled by the caller. */
+        }
     }
-    decoded = *audio_buf;
-
-    /* Get ready... Go! */
-    stereo = (MS_ADPCM_state.wavefmt.channels == 2);
-    state[0] = &MS_ADPCM_state.state[0];
-    state[1] = &MS_ADPCM_state.state[stereo];
-    while (encoded_len >= MS_ADPCM_state.wavefmt.blockalign) {
-        /* Grab the initial information for this block */
-        state[0]->hPredictor = *encoded++;
-        if (stereo) {
-            state[1]->hPredictor = *encoded++;
-        }
-        state[0]->iDelta = ((encoded[1] << 8) | encoded[0]);
-        encoded += sizeof(Sint16);
-        if (stereo) {
-            state[1]->iDelta = ((encoded[1] << 8) | encoded[0]);
-            encoded += sizeof(Sint16);
-        }
-        state[0]->iSamp1 = ((encoded[1] << 8) | encoded[0]);
-        encoded += sizeof(Sint16);
-        if (stereo) {
-            state[1]->iSamp1 = ((encoded[1] << 8) | encoded[0]);
-            encoded += sizeof(Sint16);
-        }
-        state[0]->iSamp2 = ((encoded[1] << 8) | encoded[0]);
-        encoded += sizeof(Sint16);
-        if (stereo) {
-            state[1]->iSamp2 = ((encoded[1] << 8) | encoded[0]);
-            encoded += sizeof(Sint16);
-        }
-        coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor];
-        coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor];
-
-        /* Store the two initial samples we start with */
-        decoded[0] = state[0]->iSamp2 & 0xFF;
-        decoded[1] = state[0]->iSamp2 >> 8;
-        decoded += 2;
-        if (stereo) {
-            decoded[0] = state[1]->iSamp2 & 0xFF;
-            decoded[1] = state[1]->iSamp2 >> 8;
-            decoded += 2;
-        }
-        decoded[0] = state[0]->iSamp1 & 0xFF;
-        decoded[1] = state[0]->iSamp1 >> 8;
-        decoded += 2;
-        if (stereo) {
-            decoded[0] = state[1]->iSamp1 & 0xFF;
-            decoded[1] = state[1]->iSamp1 >> 8;
-            decoded += 2;
-        }
-
-        /* Decode and store the other samples in this block */
-        samplesleft = (MS_ADPCM_state.wSamplesPerBlock - 2) *
-            MS_ADPCM_state.wavefmt.channels;
-        while (samplesleft > 0) {
-            nybble = (*encoded) >> 4;
-            new_sample = MS_ADPCM_nibble(state[0], nybble, coeff[0]);
-            decoded[0] = new_sample & 0xFF;
-            new_sample >>= 8;
-            decoded[1] = new_sample & 0xFF;
-            decoded += 2;
-
-            nybble = (*encoded) & 0x0F;
-            new_sample = MS_ADPCM_nibble(state[1], nybble, coeff[1]);
-            decoded[0] = new_sample & 0xFF;
-            new_sample >>= 8;
-            decoded[1] = new_sample & 0xFF;
-            decoded += 2;
-
-            ++encoded;
-            samplesleft -= 2;
-        }
-        encoded_len -= MS_ADPCM_state.wavefmt.blockalign;
-    }
-    SDL_free(freeable);
-    return (0);
-}
-
-struct IMA_ADPCM_decodestate
+
+    return 0;
+}
+
+static int
+WaveReadChunkData(SDL_RWops *src, WaveChunk *chunk)
 {
-    Sint32 sample;
-    Sint8 index;
+    return WaveReadPartialChunkData(src, chunk, chunk->length);
+}
+
+typedef struct WaveExtensibleGUID {
+    Uint16 encoding;
+    Uint8 guid[16];
+} WaveExtensibleGUID;
+
+/* Some of the GUIDs that are used by WAVEFORMATEXTENSIBLE. */
+#define WAVE_FORMATTAG_GUID(tag) {(tag) & 0xff, (tag) >> 8, 0, 0, 0, 0, 16, 0, 128, 0, 0, 170, 0, 56, 155, 113}
+static WaveExtensibleGUID extensible_guids[] = {
+    {PCM_CODE,        WAVE_FORMATTAG_GUID(PCM_CODE)},
+    {MS_ADPCM_CODE,   WAVE_FORMATTAG_GUID(MS_ADPCM_CODE)},
+    {IEEE_FLOAT_CODE, WAVE_FORMATTAG_GUID(IEEE_FLOAT_CODE)},
+    {ALAW_CODE,       WAVE_FORMATTAG_GUID(ALAW_CODE)},
+    {MULAW_CODE,      WAVE_FORMATTAG_GUID(MULAW_CODE)},
+    {IMA_ADPCM_CODE,  WAVE_FORMATTAG_GUID(IMA_ADPCM_CODE)}
 };
-static struct IMA_ADPCM_decoder
+
+static Uint16
+WaveGetFormatGUIDEncoding(WaveFormat *format)
 {
-    WaveFMT wavefmt;
-    Uint16 wSamplesPerBlock;
-    /* * * */
-    struct IMA_ADPCM_decodestate state[2];
-} IMA_ADPCM_state;
+    size_t i;
+    for (i = 0; i < SDL_arraysize(extensible_guids); i++) {
+        if (SDL_memcmp(format->subformat, extensible_guids[i].guid, 16) == 0) {
+            return extensible_guids[i].encoding;
+        }
+    }
+    return UNKNOWN_CODE;
+}
 
 static int
-InitIMA_ADPCM(WaveFMT * format)
+WaveReadFormat(WaveFile *file)
 {
-    Uint8 *rogue_feel;
+    WaveChunk *chunk = &file->chunk;
+    WaveFormat *format = &file->format;
+    SDL_RWops *fmtsrc;
+    size_t fmtlen = chunk->size;
 
-    /* Set the rogue pointer to the IMA_ADPCM specific data */
-    IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding);
-    IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels);
-    IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency);
-    IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate);
-    IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign);
-    IMA_ADPCM_state.wavefmt.bitspersample =
-        SDL_SwapLE16(format->bitspersample);
-    rogue_feel = (Uint8 *) format + sizeof(*format);
-    if (sizeof(*format) == 16) {
-        /* const Uint16 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); */
-        rogue_feel += sizeof(Uint16);
+    if (fmtlen > SDL_MAX_SINT32) {
+        /* Limit given by SDL_RWFromConstMem. */
+        return SDL_SetError("Data of WAVE fmt chunk too big");
+    }
+    fmtsrc = SDL_RWFromConstMem(chunk->data, (int)chunk->size);
+    if (fmtsrc == NULL) {
+        return SDL_OutOfMemory();
     }
-    IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1] << 8) | rogue_feel[0]);
-    return (0);
-}
 
-static Sint32
-IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state, Uint8 nybble)
-{
-    const Sint32 max_audioval = ((1 << (16 - 1)) - 1);
-    const Sint32 min_audioval = -(1 << (16 - 1));
-    const int index_table[16] = {
-        -1, -1, -1, -1,
-        2, 4, 6, 8,
-        -1, -1, -1, -1,
-        2, 4, 6, 8
-    };
-    const Sint32 step_table[89] = {
-        7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31,
-        34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130,
-        143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408,
-        449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282,
-        1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
-        3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630,
-        9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350,
-        22385, 24623, 27086, 29794, 32767
-    };
-    Sint32 delta, step;
+    format->formattag = SDL_ReadLE16(fmtsrc);
+    format->encoding = format->formattag;
+    format->channels = SDL_ReadLE16(fmtsrc);
+    format->frequency = SDL_ReadLE32(fmtsrc);
+    format->byterate = SDL_ReadLE32(fmtsrc);
+    format->blockalign = SDL_ReadLE16(fmtsrc);
 
-    /* Compute difference and new sample value */
-    if (state->index > 88) {
-        state->index = 88;
-    } else if (state->index < 0) {
-        state->index = 0;
+    /* This is PCM specific in the first version of the specification. */
+    if (fmtlen >= 16) {
+        format->bitspersample = SDL_ReadLE16(fmtsrc);
+    } else if (format->encoding == PCM_CODE) {
+        SDL_RWclose(fmtsrc);
+        return SDL_SetError("Missing wBitsPerSample field in WAVE fmt chunk");
     }
-    /* explicit cast to avoid gcc warning about using 'char' as array index */
-    step = step_table[(int)state->index];
-    delta = step >> 3;
-    if (nybble & 0x04)
-        delta += step;
-    if (nybble & 0x02)
-        delta += (step >> 1);
-    if (nybble & 0x01)
-        delta += (step >> 2);
-    if (nybble & 0x08)
-        delta = -delta;
-    state->sample += delta;
 
-    /* Update index value */
-    state->index += index_table[nybble];
+    /* The earlier versions also don't have this field. */
+    if (fmtlen >= 18) {
+        format->extsize = SDL_ReadLE16(fmtsrc);
+    }
 
-    /* Clamp output sample */
-    if (state->sample > max_audioval) {
-        state->sample = max_audioval;
-    } else if (state->sample < min_audioval) {
-        state->sample = min_audioval;
+    if (format->formattag == EXTENSIBLE_CODE) {
+        /* note that this ignores channel masks, smaller valid bit counts
+         * inside a larger container, and most subtypes. This is just enough
+         * to get things that didn't really _need_ WAVE_FORMAT_EXTENSIBLE
+         * to be useful working when they use this format flag.
+         */
+
+        /* Extensible header must be at least 22 bytes. */
+        if (fmtlen < 40 || format->extsize < 22) {
+            SDL_RWclose(fmtsrc);
+            return SDL_SetError("Extensible WAVE header too small");
+        }
+
+        format->validsamplebits = SDL_ReadLE16(fmtsrc);
+        format->samplesperblock = format->validsamplebits;
+        format->channelmask = SDL_ReadLE32(fmtsrc);
+        SDL_RWread(fmtsrc, format->subformat, 1, 16);
+        format->encoding = WaveGetFormatGUIDEncoding(format);
     }
-    return (state->sample);
+
+    SDL_RWclose(fmtsrc);
+
+    return 0;
 }
 
-/* Fill the decode buffer with a channel block of data (8 samples) */
-static void
-Fill_IMA_ADPCM_block(Uint8 * decoded, Uint8 * encoded,
-                     int channel, int numchannels,
-                     struct IMA_ADPCM_decodestate *state)
+static int
+WaveCheckFormat(WaveFile *file, size_t datalength)
 {
-    int i;
-    Sint8 nybble;
-    Sint32 new_sample;
+    WaveFormat *format = &file->format;
 
-    decoded += (channel * 2);
-    for (i = 0; i < 4; ++i) {
-        nybble = (*encoded) & 0x0F;
-        new_sample = IMA_ADPCM_nibble(state, nybble);
-        decoded[0] = new_sample & 0xFF;
-        new_sample >>= 8;
-        decoded[1] = new_sample & 0xFF;
-        decoded += 2 * numchannels;
+    /* Check for some obvious issues. */
 
-        nybble = (*encoded) >> 4;
-        new_sample = IMA_ADPCM_nibble(state, nybble);
-        decoded[0] = new_sample & 0xFF;
-        new_sample >>= 8;
-        decoded[1] = new_sample & 0xFF;
-        decoded += 2 * numchannels;
+    if (format->channels == 0) {
+        return SDL_SetError("Invalid number of channels");
+    } else if (format->channels > 255) {
+        /* Limit given by SDL_AudioSpec.channels. */
+        return SDL_SetError("Number of channels exceeds limit of 255");
+    }
 
-        ++encoded;
+    if (format->frequency == 0) {
+        return SDL_SetError("Invalid sample rate");
+    } else if (format->frequency > INT_MAX) {
+        /* Limit given by SDL_AudioSpec.freq. */
+        return SDL_SetError("Sample rate exceeds limit of %d", INT_MAX);
     }
-}
 
-static int
-IMA_ADPCM_decode(Uint8 ** audio_buf, Uint32 * audio_len)
-{
-    struct IMA_ADPCM_decodestate *state;
-    Uint8 *freeable, *encoded, *decoded;
-    Sint32 encoded_len, samplesleft;
-    unsigned int c, channels;
-
-    /* Check to make sure we have enough variables in the state array */
-    channels = IMA_ADPCM_state.wavefmt.channels;
-    if (channels > SDL_arraysize(IMA_ADPCM_state.state)) {
-        SDL_SetError("IMA ADPCM decoder can only handle %u channels",
-                     (unsigned int)SDL_arraysize(IMA_ADPCM_state.state));
-        return (-1);
-    }
-    state = IMA_ADPCM_state.state;
-
-    /* Allocate the proper sized output buffer */
-    encoded_len = *audio_len;
-    encoded = *audio_buf;
-    freeable = *audio_buf;
-    *audio_len = (encoded_len / IMA_ADPCM_state.wavefmt.blockalign) *
-        IMA_ADPCM_state.wSamplesPerBlock *
-        IMA_ADPCM_state.wavefmt.channels * sizeof(Sint16);
-    *audio_buf = (Uint8 *) SDL_malloc(*audio_len);
-    if (*audio_buf == NULL) {
-        return SDL_OutOfMemory();
+    /* Reject invalid fact chunks in strict mode. */
+    if (file->facthint == FactStrict && file->fact.status == -1) {
+        return SDL_SetError("Invalid fact chunk in WAVE file");
     }
-    decoded = *audio_buf;
-
-    /* Get ready... Go! */
-    while (encoded_len >= IMA_ADPCM_state.wavefmt.blockalign) {
-        /* Grab the initial information for this block */
-        for (c = 0; c < channels; ++c) {
-            /* Fill the state information for this block */
-            state[c].sample = ((encoded[1] << 8) | encoded[0]);
-            encoded += 2;
-            if (state[c].sample & 0x8000) {
-                state[c].sample -= 0x10000;
-            }
-            state[c].index = *encoded++;
-            /* Reserved byte in buffer header, should be 0 */
-            if (*encoded++ != 0) {
-                /* Uh oh, corrupt data?  Buggy code? */ ;
-            }
 
-            /* Store the initial sample we start with */
-            decoded[0] = (Uint8) (state[c].sample & 0xFF);
-            decoded[1] = (Uint8) (state[c].sample >> 8);
-            decoded += 2;
+    /* Check for issues common to all encodings. Some unsupported formats set
+     * the bits per sample to zero. These fall through to the 'unsupported
+     * format' error.
+     */
+    switch (format->encoding) {
+    case IEEE_FLOAT_CODE:
+    case ALAW_CODE:
+    case MULAW_CODE:
+    case MS_ADPCM_CODE:
+    case IMA_ADPCM_CODE:
+        /* These formats require a fact chunk. */
+        if (file->facthint == FactStrict && file->fact.status <= 0) {
+            return SDL_SetError("Missing fact chunk in WAVE file");
+        }
+        /* fallthrough */
+    case PCM_CODE:
+        /* All supported formats require a non-zero bit depth. */
+        if (file->chunk.size < 16) {
+            return SDL_SetError("Missing wBitsPerSample field in WAVE fmt chunk");
+        } else if (format->bitspersample == 0) {
+            return SDL_SetError("Invalid bits per sample");
+        }
+
+        /* All supported formats must have a proper block size. */
+        if (format->blockalign == 0) {
+            return SDL_SetError("Invalid block alignment");
         }
 
-        /* Decode and store the other samples in this block */
-        samplesleft = (IMA_ADPCM_state.wSamplesPerBlock - 1) * channels;
-        while (samplesleft > 0) {
-            for (c = 0; c < channels; ++c) {
-                Fill_IMA_ADPCM_block(decoded, encoded,
-                                     c, channels, &state[c]);
-                encoded += 4;
-                samplesleft -= 8;
+        /* If the fact chunk is valid and the appropriate hint is set, the
+         * decoders will use the number of sample frames from the fact chunk.
+         */
+        if (file->fact.status == 1) {
+            WaveFactChunkHint hint = file->facthint;
+            Uint32 samples = file->fact.samplelength;
+            if (hint == FactTruncate || hint == FactStrict || (hint == FactIgnoreZero && samples > 0)) {
+                file->fact.status = 2;
             }
-            decoded += (channels * 8 * 2);
         }
-        encoded_len -= IMA_ADPCM_state.wavefmt.blockalign;
     }
-    SDL_free(freeable);
-    return (0);
-}
 
+    /* Check the format for encoding specific issues and initialize decoders. */
+    switch (format->encoding) {
+    case PCM_CODE:
+    case IEEE_FLOAT_CODE:
+        if (PCM_Init(file, datalength) < 0) {
+            return -1;
+        }
+        break;
+    case ALAW_CODE:
+    case MULAW_CODE:
+        if (LAW_Init(file, datalength) < 0) {
+            return -1;
+        }
+        break;
+    case MS_ADPCM_CODE:
+        if (MS_ADPCM_Init(file, datalength) < 0) {
+            return -1;
+        }
+        break;
+    case IMA_ADPCM_CODE:
+        if (IMA_ADPCM_Init(file, datalength) < 0) {
+            return -1;
+        }
+        break;
+    case MPEG_CODE:
+    case MPEGLAYER3_CODE:
+        return SDL_SetError("MPEG formats not supported");
+    default:
+        if (format->formattag == EXTENSIBLE_CODE) {
+            const char *errstr = "Unknown WAVE format GUID: %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x";
+            const Uint8 *g = format->subformat;
+            const Uint32 g1 = g[0] | ((Uint32)g[1] << 8) | ((Uint32)g[2] << 16) | ((Uint32)g[3] << 24);
+            const Uint32 g2 = g[4] | ((Uint32)g[5] << 8);
+            const Uint32 g3 = g[6] | ((Uint32)g[7] << 8);
+            return SDL_SetError(errstr, g1, g2, g3, g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15]);
+        }
+        return SDL_SetError("Unknown WAVE format tag: 0x%04x", (unsigned int)format->encoding);
+    }
+
+    return 0;
+}
 
 static int
-ConvertSint24ToSint32(Uint8 ** audio_buf, Uint32 * audio_len)
-{
-    const double DIVBY8388608 = 0.00000011920928955078125;
-    const Uint32 original_len = *audio_len;
-    const Uint32 samples = original_len / 3;
-    const Uint32 expanded_len = samples * sizeof (Uint32);
-    Uint8 *ptr = (Uint8 *) SDL_realloc(*audio_buf, expanded_len);
-    const Uint8 *src;
-    Uint32 *dst;
-    Uint32 i;
-
-    if (!ptr) {
-        return SDL_OutOfMemory();
+WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
+{
+    int result;
+    Uint32 chunkcount = 0;
+    Uint32 chunkcountlimit = 10000;
+    char *envchunkcountlimit;
+    Sint64 RIFFstart, RIFFend, lastchunkpos;
+    SDL_bool RIFFlengthknown = SDL_FALSE;
+    WaveFormat *format = &file->format;
+    WaveChunk *chunk = &file->chunk;
+    WaveChunk RIFFchunk;
+    WaveChunk fmtchunk;
+    WaveChunk datachunk;
+
+    SDL_zero(RIFFchunk);
+    SDL_zero(fmtchunk);
+    SDL_zero(datachunk);
+
+    envchunkcountlimit = SDL_getenv("SDL_WAVE_CHUNK_LIMIT");
+    if (envchunkcountlimit != NULL) {
+        unsigned int count;
+        if (SDL_sscanf(envchunkcountlimit, "%u", &count) == 1) {
+            chunkcountlimit = count <= SDL_MAX_UINT32 ? count : SDL_MAX_UINT32;
+        }
     }
 
-    *audio_buf = ptr;
-    *audio_len = expanded_len;
+    RIFFstart = SDL_RWtell(src);
+    if (RIFFstart < 0) {
+        return SDL_SetError("Could not seek in file");
+    }
 
-    /* work from end to start, since we're expanding in-place. */
-    src = (ptr + original_len) - 3;
-    dst = ((Uint32 *) (ptr + expanded_len)) - 1;
-    for (i = 0; i < samples; i++) {
-        /* There's probably a faster way to do all this. */
-        const Sint32 converted = ((Sint32) ( (((Uint32) src[2]) << 24) |
-                                             (((Uint32) src[1]) << 16) |
-                                             (((Uint32) src[0]) << 8) )) >> 8;
-        const double scaled = (((double) converted) * DIVBY8388608);
-        src -= 3;
-        *(dst--) = (Sint32) (scaled * 2147483647.0);
+    RIFFchunk.position = RIFFstart;
+    if (WaveNextChunk(src, &RIFFchunk) < 0) {
+        return SDL_SetError("Could not read RIFF header");
     }
 
-    return 0;
-}
+    /* Check main WAVE file identifiers. */
+    if (RIFFchunk.fourcc == RIFF) {
+        Uint32 formtype;
+        /* Read the form type. "WAVE" expected. */
+        if (SDL_RWread(src, &formtype, sizeof(Uint32), 1) != 1) {
+            return SDL_SetError("Could not read RIFF form type");
+        } else if (SDL_SwapLE32(formtype) != WAVE) {
+            return SDL_SetError("RIFF form type is not WAVE (not a Waveform file)");
+        }
+    } else if (RIFFchunk.fourcc == WAVE) {
+        /* RIFF chunk missing or skipped. Length unknown. */
+        RIFFchunk.position = 0;
+        RIFFchunk.length = 0;
+    } else {
+        return SDL_SetError("Could not find RIFF or WAVE identifiers (not a Waveform file)");
+    }
 
+    /* The 4-byte form type is immediately followed by the first chunk.*/
+    chunk->position = RIFFchunk.position + 4;
 
-/* GUIDs that are used by WAVE_FORMAT_EXTENSIBLE */
-static const Uint8 extensible_pcm_guid[16] = { 1, 0, 0, 0, 0, 0, 16, 0, 128, 0, 0, 170, 0, 56, 155, 113 };
-static const Uint8 extensible_ieee_guid[16] = { 3, 0, 0, 0, 0, 0, 16, 0, 128, 0, 0, 170, 0, 56, 155, 113 };
+    /* Use the RIFF chunk size to limit the search for the chunks. This is not
+     * always reliable and the hint can be used to tune the behavior. By
+     * default, it will never search past 4 GiB.
+     */
+    switch (file->riffhint) {
+    case RiffSizeIgnore:
+        RIFFend = RIFFchunk.position + SDL_MAX_UINT32;
+        break;
+    default:
+    case RiffSizeIgnoreZero:
+        if (RIFFchunk.length == 0) {
+            RIFFend = RIFFchunk.position + SDL_MAX_UINT32;
+            break;
+        }
+        /* fallthrough */
+    case RiffSizeForce:
+        RIFFend = RIFFchunk.position + RIFFchunk.length;
+        RIFFlengthknown = SDL_TRUE;
+        break;
+    case RiffSizeMaximum:
+        RIFFend = SDL_MAX_SINT64;
+        break;
+    }
 
-SDL_AudioSpec *
-SDL_LoadWAV_RW(SDL_RWops * src, int freesrc,
-               SDL_AudioSpec * spec, Uint8 ** audio_buf, Uint32 * audio_len)
-{
-    int was_error;
-    Chunk chunk;
-    int lenread;
-    int IEEE_float_encoded, MS_ADPCM_encoded, IMA_ADPCM_encoded;
-    int samplesize;
+    /* Step through all chunks and save information on the fmt, data, and fact
+     * chunks. Ignore the chunks we don't know as per specification. This
+     * currently also ignores cue, list, and slnt chunks.
+     */
+    while ((Uint64)RIFFend > (Uint64)chunk->position + chunk->length + (chunk->length & 1)) {
+        /* Abort after too many chunks or else corrupt files may waste time. */
+        if (chunkcount++ >= chunkcountlimit) {
+            return SDL_SetError("Chunk count in WAVE file exceeds limit of %u", chunkcountlimit);
+        }
+
+        result = WaveNextChunk(src, chunk);
+        if (result == -1) {
+            /* Unexpected EOF. Corrupt file or I/O issues. */
+            if (file->trunchint == TruncVeryStrict) {
+                return SDL_SetError("Unexpected end of WAVE file");
+            }
+            /* Let the checks after this loop sort this issue out. */
+            break;
+        } else if (result == -2) {
+            return SDL_SetError("Could not seek to WAVE chunk header");
+        }
 
-    /* WAV magic header */
-    Uint32 RIFFchunk;
-    Uint32 wavelen = 0;
-    Uint32 WAVEmagic;
-    Uint32 headerDiff = 0;
+        if (chunk->fourcc == FMT) {
+            if (fmtchunk.fourcc == FMT) {
+                /* Multiple fmt chunks. Ignore or error? */
+            } else {
+                /* The fmt chunk must occur before the data chunk. */
+                if (datachunk.fourcc == DATA) {
+                    return SDL_SetError("fmt chunk after data chunk in WAVE file");
+                }
+                fmtchunk = *chunk;
+            }
+        } else if (chunk->fourcc == DATA) {
+            /* Only use the first data chunk. Handling the wavl list madness
+             * may require a different approach.
+             */
+            if (datachunk.fourcc != DATA) {
+                datachunk = *chunk;
+            }
+        } else if (chunk->fourcc == FACT) {
+            /* The fact chunk data must be at least 4 bytes for the
+             * dwSampleLength field. Ignore all fact chunks after the first one.
+             */
+            if (file->fact.status == 0) {
+                if (chunk->length < 4) {
+                    file->fact.status = -1;
+                } else {
+                    /* Let's use src directly, it's just too convenient. */
+                    Sint64 position = SDL_RWseek(src, chunk->position, RW_SEEK_SET);
+                    Uint32 samplelength;
+                    if (position == chunk->position && SDL_RWread(src, &samplelength, sizeof(Uint32), 1) == 1) {
+                        file->fact.status = 1;
+                        file->fact.samplelength = SDL_SwapLE32(samplelength);
+                    } else {
+                        file->fact.status = -1;
+                    }
+                }
+            }
+        }
 
-    /* FMT chunk */
-    WaveFMT *format = NULL;
-    WaveExtensibleFMT *ext = NULL;
+        /* Go through all chunks in verystrict mode or stop the search early if
+         * all required chunks were found.
+         */
+        if (file->trunchint == TruncVeryStrict) {
+            if ((Uint64)RIFFend < (Uint64)chunk->position + chunk->length) {
+                return SDL_SetError("RIFF size truncates chunk");
+            }
+        } else if (fmtchunk.fourcc == FMT && datachunk.fourcc == DATA) {
+            if (file->fact.status == 1 || file->facthint == FactIgnore || file->facthint == FactNoHint) {
+                break;
+            }
+        }
+    }
 
-    SDL_zero(chunk);
+    /* Save the position after the last chunk. This position will be used if the
+     * RIFF length is unknown.
+     */
+    lastchunkpos = chunk->position + chunk->length;
 
-    /* Make sure we are passed a valid data source */
-    was_error = 0;
-    if (src == NULL) {
-        was_error = 1;
-        goto done;
+    /* The fmt chunk is mandatory. */
+    if (fmtchunk.fourcc != FMT) {
+        return SDL_SetError("Missing fmt chunk in WAVE file");
+    }
+    /* A data chunk must be present. */
+    if (datachunk.fourcc != DATA) {
+        return SDL_SetError("Missing data chunk in WAVE file");
+    }
+    /* Check if the last chunk has all of its data in verystrict mode. */
+    if (file->trunchint == TruncVeryStrict) {
+        /* data chunk is handled later. */
+        if (chunk->fourcc != DATA && chunk->length > 0) {
+            Uint8 tmp;
+            Uint64 position = (Uint64)chunk->position + chunk->length - 1;
+            if (position > SDL_MAX_SINT64 || SDL_RWseek(src, (Sint64)position, RW_SEEK_SET) != (Sint64)position) {
+                return SDL_SetError("Could not seek to WAVE chunk data");
+            } else if (SDL_RWread(src, &tmp, 1, 1) != 1) {
+                return SDL_SetError("RIFF size truncates chunk");
+            }
+        }
     }
 
-    /* Check the magic header */
-    RIFFchunk = SDL_ReadLE32(src);
-    wavelen = SDL_ReadLE32(src);
-    if (wavelen == WAVE) {      /* The RIFFchunk has already been read */
-        WAVEmagic = wavelen;
-        wavelen = RIFFchunk;
-        RIFFchunk = RIFF;
-    } else {
-        WAVEmagic = SDL_ReadLE32(src);
-    }
-    if ((RIFFchunk != RIFF) || (WAVEmagic != WAVE)) {
-        SDL_SetError("Unrecognized file type (not WAVE)");
-        was_error = 1;
-        goto done;
-    }
-    headerDiff += sizeof(Uint32);       /* for WAVE */
-
-    /* Read the audio data format chunk */
-    chunk.data = NULL;
-    do {
-        SDL_free(chunk.data);
-        chunk.data = NULL;
-        lenread = ReadChunk(src, &chunk);
-        if (lenread < 0) {
-            was_error = 1;
-            goto done;
-        }
-        /* 2 Uint32's for chunk header+len, plus the lenread */
-        headerDiff += lenread + 2 * sizeof(Uint32);
-    } while ((chunk.magic == FACT) || (chunk.magic == LIST) || (chunk.magic == BEXT) || (chunk.magic == JUNK));
-
-    /* Decode the audio data format */
-    format = (WaveFMT *) chunk.data;
-    if (chunk.magic != FMT) {
-        SDL_SetError("Complex WAVE files not supported");
-        was_error = 1;
-        goto done;
-    }
-    IEEE_float_encoded = MS_ADPCM_encoded = IMA_ADPCM_encoded = 0;
-    switch (SDL_SwapLE16(format->encoding)) {
+    /* Process fmt chunk. */
+    *chunk = fmtchunk;
+
+    /* No need to read more than 1046 bytes of the fmt chunk data with the
+     * formats that are currently supported. (1046 because of MS ADPCM coefficients)
+     */
+    if (WaveReadPartialChunkData(src, chunk, 1046) < 0) {
+        return SDL_SetError("Could not read data of WAVE fmt chunk");
+    }
+
+    /* The fmt chunk data must be at least 14 bytes to include all common fields.
+     * It usually is 16 and larger depending on the header and encoding.
+     */
+    if (chunk->length < 14) {
+        return SDL_SetError("Invalid WAVE fmt chunk length (too small)");
+    } else if (chunk->size < 14) {
+        return SDL_SetError("Could not read data of WAVE fmt chunk");
+    } else if (WaveReadFormat(file) < 0) {
+        return -1;
+    } else if (WaveCheckFormat(file, (size_t)datachunk.length) < 0) {
+        return -1;
+    }
+
+#ifdef SDL_WAVE_DEBUG_LOG_FORMAT
+    WaveDebugLogFormat(file);
+#endif
+#ifdef SDL_WAVE_DEBUG_DUMP_FORMAT
+    WaveDebugDumpFormat(file, RIFFchunk.length, fmtchunk.length, datachunk.length);
+#endif
+
+    WaveFreeChunkData(chunk);
+
+    /* Process data chunk. */
+    *chunk = datachunk;
+
+    if (chunk->length > 0) {
+        result = WaveReadChunkData(src, chunk);
+        if (result == -1) {
+            return -1;
+        } else if (result == -2) {
+            return SDL_SetError("Could not seek data of WAVE data chunk");
+        }
+    }
+
+    if (chunk->length != chunk->size) {
+        /* I/O issues or corrupt file. */
+        if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) {
+            return SDL_SetError("Could not read data of WAVE data chunk");
+        }
+        /* The decoders handle this truncation. */
+    }
+
+    /* Decode or convert the data if necessary. */
+    switch (format->encoding) {
     case PCM_CODE:
-        /* We can understand this */
-        break;
     case IEEE_FLOAT_CODE:
-        IEEE_float_encoded = 1;
-        /* We can understand this */
+        if (PCM_Decode(file, audio_buf, audio_len) < 0) {
+            return -1;
+        }
         break;
-    case MS_ADPCM_CODE:
-        /* Try to understand this */
-        if (InitMS_ADPCM(format) < 0) {
-            was_error = 1;
-            goto done;
+    case ALAW_CODE:
+    case MULAW_CODE:
+        if (LAW_Decode(file, audio_buf, audio_len) < 0) {
+            return -1;
         }
-        MS_ADPCM_encoded = 1;
         break;
-    case IMA_ADPCM_CODE:
-        /* Try to understand this */
-        if (InitIMA_ADPCM(format) < 0) {
-            was_error = 1;
-            goto done;
+    case MS_ADPCM_CODE:
+        if (MS_ADPCM_Decode(file, audio_buf, audio_len) < 0) {
+            return -1;
         }
-        IMA_ADPCM_encoded = 1;
         break;
-    case EXTENSIBLE_CODE:
-        /* note that this ignores channel masks, smaller valid bit counts
-           inside a larger container, and most subtypes. This is just enough
-           to get things that didn't really _need_ WAVE_FORMAT_EXTENSIBLE
-           to be useful working when they use this format flag. */
-        ext = (WaveExtensibleFMT *) format;
-        if (SDL_SwapLE16(ext->size) < 22) {
-            SDL_SetError("bogus extended .wav header");
-            was_error = 1;
-            goto done;
-        }
-        if (SDL_memcmp(ext->subformat, extensible_pcm_guid, 16) == 0) {
-            break;  /* cool. */
-        } else if (SDL_memcmp(ext->subformat, extensible_ieee_guid, 16) == 0) {
-            IEEE_float_encoded = 1;
-            break;
+    case IMA_ADPCM_CODE:
+        if (IMA_ADPCM_Decode(file, audio_buf, audio_len) < 0) {
+            return -1;
         }
         break;
-    case MP3_CODE:
-        SDL_SetError("MPEG Layer 3 data not supported");
-        was_error = 1;
-        goto done;
-    default:
-        SDL_SetError("Unknown WAVE data format: 0x%.4x",
-                     SDL_SwapLE16(format->encoding));
-        was_error = 1;
-        goto done;
     }
+
+    /* Setting up the SDL_AudioSpec. All unsupported formats were filtered out
+     * by checks earlier in this function.
+     */
     SDL_zerop(spec);
-    spec->freq = SDL_SwapLE32(format->frequency);
+    spec->freq = format->frequency;
+    spec->channels = (Uint8)format->channels;
+    spec->samples = 4096;       /* Good default buffer size */
 
-    if (IEEE_float_encoded) {
-        if ((SDL_SwapLE16(format->bitspersample)) != 32) {
-            was_error = 1;
-        } else {
-            spec->format = AUDIO_F32;
-        }
-    } else {
-        switch (SDL_SwapLE16(format->bitspersample)) {
-        case 4:
-            if (MS_ADPCM_encoded || IMA_ADPCM_encoded) {
-                spec->format = AUDIO_S16;
-            } else {
-                was_error = 1;
-            }
-            break;
+    switch (format->encoding) {
+    case MS_ADPCM_CODE:
+    case IMA_ADPCM_CODE:
+    case ALAW_CODE:
+    case MULAW_CODE:
+        /* These can be easily stored in the byte order of the system. */
+        spec->format = AUDIO_S16SYS;
+        break;
+    case IEEE_FLOAT_CODE:
+        spec->format = AUDIO_F32LSB;
+        break;
+    case PCM_CODE:
+        switch (format->bitspersample) {
         case 8:
             spec->format = AUDIO_U8;
             break;
         case 16:
-            spec->format = AUDIO_S16;
-            break;
-        case 24:  /* convert this. */
-            spec->format = AUDIO_S32;
+            spec->format = AUDIO_S16LSB;
             break;
+        case 24: /* Has been shifted to 32 bits. */
         case 32:
-            spec->format = AUDIO_S32;
+            spec->format = AUDIO_S32LSB;
             break;
         default:
-            was_error = 1;
-            break;
+            /* Just in case something unexpected happened in the checks. */
+            return SDL_SetError("Unexpected %u-bit PCM data format", (unsigned int)format->bitspersample);
         }
+        break;
     }
 
-    if (was_error) {
-        SDL_SetError("Unknown %d-bit PCM data format",
-                     SDL_SwapLE16(format->bitspersample));
-        goto done;
+    /* Report the end position back to the cleanup code. */
+    if (RIFFlengthknown) {
+        chunk->position = RIFFend;
+    } else {
+        chunk->position = lastchunkpos;
     }
-    spec->channels = (Uint8) SDL_SwapLE16(format->channels);
-    spec->samples = 4096;       /* Good default buffer size */
 
-    /* Read the audio data chunk */
-    *audio_buf = NULL;
-    do {
-        SDL_free(*audio_buf);
-        *audio_buf = NULL;
-        lenread = ReadChunk(src, &chunk);
-        if (lenread < 0) {
-            was_error = 1;
-            goto done;
-        }
-        *audio_len = lenread;
-        *audio_buf = chunk.data;
-        if (chunk.magic != DATA)
-            headerDiff += lenread + 2 * sizeof(Uint32);
-    } while (chunk.magic != DATA);
-    headerDiff += 2 * sizeof(Uint32);   /* for the data chunk and len */
+    return 0;
+}
 
-    if (MS_ADPCM_encoded) {
-        if (MS_ADPCM_decode(audio_buf, audio_len) < 0) {
-            was_error = 1;
-            goto done;
-        }
-    }
-    if (IMA_ADPCM_encoded) {
-        if (IMA_ADPCM_decode(audio_buf, audio_len) < 0) {
-            was_error = 1;
-            goto done;
-        }
-    }
+SDL_AudioSpec *
+SDL_LoadWAV_RW(SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
+{
+    int result;
+    WaveFile file;
 
-    if (SDL_SwapLE16(format->bitspersample) == 24) {
-        if (ConvertSint24ToSint32(audio_buf, audio_len) < 0) {
-            was_error = 1;
-            goto done;
-        }
+    SDL_zero(file);
+
+    /* Make sure we are passed a valid data source */
+    if (src == NULL) {
+        /* Error may come from RWops. */
+        return NULL;
+    } else if (spec == NULL) {
+        SDL_InvalidParamError("spec");
+        return NULL;
+    } else if (audio_buf == NULL) {
+        SDL_InvalidParamError("audio_buf");
+        return NULL;
+    } else if (audio_len == NULL) {
+        SDL_InvalidParamError("audio_len");
+        return NULL;
     }
 
-    /* Don't return a buffer that isn't a multiple of samplesize */
-    samplesize = ((SDL_AUDIO_BITSIZE(spec->format)) / 8) * spec->channels;
-    *audio_len &= ~(samplesize - 1);
+    *audio_buf = NULL;
+    *audio_len = 0;
 
-  done:
-    SDL_free(format);
-    if (src) {
-        if (freesrc) {
-            SDL_RWclose(src);
-        } else {
-            /* seek to the end of the file (given by the RIFF chunk) */
-            SDL_RWseek(src, wavelen - chunk.length - headerDiff, RW_SEEK_CUR);
-        }
-    }
-    if (was_error) {
+    file.riffhint = WaveGetRiffSizeHint();
+    file.trunchint = WaveGetTruncationHint();
+    file.facthint = WaveGetFactChunkHint();
+
+    result = WaveLoad(src, &file, spec, audio_buf, audio_len);
+    if (result < 0) {
+        SDL_free(*audio_buf);
         spec = NULL;
+        audio_buf = NULL;
+        audio_len = 0;
+    }
+
+    /* Cleanup */
+    if (freesrc) {
+        SDL_RWclose(src);
+    } else {
+        SDL_RWseek(src, file.chunk.position, RW_SEEK_SET);
     }
-    return (spec);
+    WaveFreeChunkData(&file.chunk);
+    SDL_free(file.decoderdata);
+
+    return spec;
 }
 
 /* Since the WAV memory is allocated in the shared library, it must also
    be freed here.  (Necessary under Win32, VC++)
  */
 void
-SDL_FreeWAV(Uint8 * audio_buf)
+SDL_FreeWAV(Uint8 *audio_buf)
 {
     SDL_free(audio_buf);
 }
 
-static int
-ReadChunk(SDL_RWops * src, Chunk * chunk)
-{
-    chunk->magic = SDL_ReadLE32(src);
-    chunk->length = SDL_ReadLE32(src);
-    chunk->data = (Uint8 *) SDL_malloc(chunk->length);
-    if (chunk->data == NULL) {
-        return SDL_OutOfMemory();
-    }
-    if (SDL_RWread(src, chunk->data, chunk->length, 1) != 1) {
-        SDL_free(chunk->data);
-        chunk->data = NULL;
-        return SDL_Error(SDL_EFREAD);
-    }
-    return (chunk->length);
-}
-
 /* vi: set ts=4 sw=4 expandtab: */

+ 104 - 32
Source/ThirdParty/SDL/src/audio/SDL_wave.h

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -20,11 +20,12 @@
 */
 #include "../SDL_internal.h"
 
-/* WAVE files are little-endian */
+/* RIFF WAVE files are little-endian */
 
 /*******************************************/
 /* Define values for Microsoft WAVE format */
 /*******************************************/
+/* FOURCC */
 #define RIFF            0x46464952      /* "RIFF" */
 #define WAVE            0x45564157      /* "WAVE" */
 #define FACT            0x74636166      /* "fact" */
@@ -33,45 +34,116 @@
 #define JUNK            0x4B4E554A      /* "JUNK" */
 #define FMT             0x20746D66      /* "fmt " */
 #define DATA            0x61746164      /* "data" */
+/* Format tags */
+#define UNKNOWN_CODE    0x0000
 #define PCM_CODE        0x0001
 #define MS_ADPCM_CODE   0x0002
 #define IEEE_FLOAT_CODE 0x0003
+#define ALAW_CODE       0x0006
+#define MULAW_CODE      0x0007
 #define IMA_ADPCM_CODE  0x0011
-#define MP3_CODE        0x0055
+#define MPEG_CODE       0x0050
+#define MPEGLAYER3_CODE 0x0055
 #define EXTENSIBLE_CODE 0xFFFE
-#define WAVE_MONO       1
-#define WAVE_STEREO     2
 
-/* Normally, these three chunks come consecutively in a WAVE file */
-typedef struct WaveFMT
+/* Stores the WAVE format information. */
+typedef struct WaveFormat
 {
-/* Not saved in the chunk we read:
-    Uint32  FMTchunk;
-    Uint32  fmtlen;
-*/
-    Uint16 encoding;
-    Uint16 channels;            /* 1 = mono, 2 = stereo */
-    Uint32 frequency;           /* One of 11025, 22050, or 44100 Hz */
-    Uint32 byterate;            /* Average bytes per second */
-    Uint16 blockalign;          /* Bytes per sample block */
-    Uint16 bitspersample;       /* One of 8, 12, 16, or 4 for ADPCM */
-} WaveFMT;
-
-/* The general chunk found in the WAVE file */
-typedef struct Chunk
+    Uint16 formattag;       /* Raw value of the first field in the fmt chunk data. */
+    Uint16 encoding;        /* Actual encoding, possibly from the extensible header. */
+    Uint16 channels;        /* Number of channels. */
+    Uint32 frequency;       /* Sampling rate in Hz. */
+    Uint32 byterate;        /* Average bytes per second. */
+    Uint16 blockalign;      /* Bytes per block. */
+    Uint16 bitspersample;   /* Currently supported are 8, 16, 24, 32, and 4 for ADPCM. */
+
+    /* Extra information size. Number of extra bytes starting at byte 18 in the
+     * fmt chunk data. This is at least 22 for the extensible header.
+     */
+    Uint16 extsize;
+
+    /* Extensible WAVE header fields */
+    Uint16 validsamplebits;
+    Uint32 samplesperblock; /* For compressed formats. Can be zero. Actually 16 bits in the header. */
+    Uint32 channelmask;
+    Uint8 subformat[16];    /* A format GUID. */
+} WaveFormat;
+
+/* Stores information on the fact chunk. */
+typedef struct WaveFact {
+    /* Represents the state of the fact chunk in the WAVE file.
+     * Set to -1 if the fact chunk is invalid.
+     * Set to 0 if the fact chunk is not present
+     * Set to 1 if the fact chunk is present and valid.
+     * Set to 2 if samplelength is going to be used as the number of sample frames.
+     */
+    Sint32 status;
+
+    /* Version 1 of the RIFF specification calls the field in the fact chunk
+     * dwFileSize. The Standards Update then calls it dwSampleLength and specifies
+     * that it is 'the length of the data in samples'. WAVE files from Windows
+     * with this chunk have it set to the samples per channel (sample frames).
+     * This is useful to truncate compressed audio to a specific sample count
+     * because a compressed block is usually decoded to a fixed number of
+     * sample frames.
+     */
+    Uint32 samplelength; /* Raw sample length value from the fact chunk. */
+} WaveFact;
+
+/* Generic struct for the chunks in the WAVE file. */
+typedef struct WaveChunk
 {
-    Uint32 magic;
-    Uint32 length;
-    Uint8 *data;
-} Chunk;
+    Uint32 fourcc;   /* FOURCC of the chunk. */
+    Uint32 length;   /* Size of the chunk data. */
+    Sint64 position; /* Position of the data in the stream. */
+    Uint8 *data;     /* When allocated, this points to the chunk data. length is used for the malloc size. */
+    size_t size;     /* Number of bytes in data that could be read from the stream. Can be smaller than length. */
+} WaveChunk;
 
-typedef struct WaveExtensibleFMT
+/* Controls how the size of the RIFF chunk affects the loading of a WAVE file. */
+typedef enum WaveRiffSizeHint {
+    RiffSizeNoHint,
+    RiffSizeForce,
+    RiffSizeIgnoreZero,
+    RiffSizeIgnore,
+    RiffSizeMaximum
+} WaveRiffSizeHint;
+
+/* Controls how a truncated WAVE file is handled. */
+typedef enum WaveTruncationHint {
+    TruncNoHint,
+    TruncVeryStrict,
+    TruncStrict,
+    TruncDropFrame,
+    TruncDropBlock
+} WaveTruncationHint;
+
+/* Controls how the fact chunk affects the loading of a WAVE file. */
+typedef enum WaveFactChunkHint {
+    FactNoHint,
+    FactTruncate,
+    FactStrict,
+    FactIgnoreZero,
+    FactIgnore
+} WaveFactChunkHint;
+
+typedef struct WaveFile
 {
-    WaveFMT format;
-    Uint16 size;
-    Uint16 validbits;
-    Uint32 channelmask;
-    Uint8 subformat[16];  /* a GUID. */
-} WaveExtensibleFMT;
+    WaveChunk chunk;
+    WaveFormat format;
+    WaveFact fact;
+
+    /* Number of sample frames that will be decoded. Calculated either with the
+     * size of the data chunk or, if the appropriate hint is enabled, with the
+     * sample length value from the fact chunk.
+     */
+    Sint64 sampleframes;
+
+    void *decoderdata;   /* Some decoders require extra data for a state. */
+
+    WaveRiffSizeHint riffhint;
+    WaveTruncationHint trunchint;
+    WaveFactChunkHint facthint;
+} WaveFile;
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 65 - 96
Source/ThirdParty/SDL/src/audio/alsa/SDL_alsa_audio.c

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2017 Sam Lantinga <[email protected]>
+  Copyright (C) 1997-2019 Sam Lantinga <[email protected]>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -22,6 +22,10 @@
 
 #if SDL_AUDIO_DRIVER_ALSA
 
+#ifndef SDL_ALSA_NON_BLOCKING
+#define SDL_ALSA_NON_BLOCKING 0
+#endif
+
 /* Allow access to a raw mixing buffer */
 
 #include <sys/types.h>
@@ -68,7 +72,9 @@ static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
 static int (*ALSA_snd_pcm_hw_params_get_period_size)
   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
-static int (*ALSA_snd_pcm_hw_params_set_periods_near)
+static int (*ALSA_snd_pcm_hw_params_set_periods_min)
+  (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
+static int (*ALSA_snd_pcm_hw_params_set_periods_first)
   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
 static int (*ALSA_snd_pcm_hw_params_get_periods)
   (const snd_pcm_hw_params_t *, unsigned int *, int *);
@@ -90,6 +96,7 @@ static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
 static int (*ALSA_snd_device_name_hint) (int, const char *, void ***);
 static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *);
 static int (*ALSA_snd_device_name_free_hint) (void **);
+static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
 #ifdef SND_CHMAP_API_VERSION
 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
 static int (*ALSA_snd_pcm_chmap_print) (const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
@@ -143,7 +150,8 @@ load_alsa_syms(void)
     SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
     SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
     SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
-    SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near);
+    SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min);
+    SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first);
     SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
     SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
     SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
@@ -158,6 +166,7 @@ load_alsa_syms(void)
     SDL_ALSA_SYM(snd_device_name_hint);
     SDL_ALSA_SYM(snd_device_name_get_hint);
     SDL_ALSA_SYM(snd_device_name_free_hint);
+    SDL_ALSA_SYM(snd_pcm_avail);
 #ifdef SND_CHMAP_API_VERSION
     SDL_ALSA_SYM(snd_pcm_get_chmap);
     SDL_ALSA_SYM(snd_pcm_chmap_print);
@@ -243,7 +252,24 @@ get_audio_device(void *handle, const int channels)
 static void
 ALSA_WaitDevice(_THIS)
 {
-    /* We're in blocking mode, so there's nothing to do here */
+#if SDL_ALSA_NON_BLOCKING
+    const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->spec.samples;
+    while (SDL_AtomicGet(&this->enabled)) {
+        const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle);
+        if ((rc < 0) && (rc != -EAGAIN)) {
+            /* Hmm, not much we can do - abort */
+            fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
+                        ALSA_snd_strerror(rc));
+            SDL_OpenedAudioDeviceDisconnected(this);
+            return;
+        } else if (rc < needed) {
+            const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / this->spec.freq;
+            SDL_Delay(SDL_max(delay, 10));
+        } else {
+            break;  /* ready to go! */
+        }
+    }
+#endif
 }
 
 
@@ -422,7 +448,7 @@ static void
 ALSA_CloseDevice(_THIS)
 {
     if (this->hidden->pcm_handle) {
-	/* Wait for the submitted audio to drain
+        /* Wait for the submitted audio to drain
            ALSA_snd_pcm_drop() can hang, so don't use that.
          */
         Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2;
@@ -435,115 +461,59 @@ ALSA_CloseDevice(_THIS)
 }
 
 static int
-ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
-{
-    int status;
-    snd_pcm_uframes_t bufsize;
-
-    /* "set" the hardware with the desired parameters */
-    status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
-    if ( status < 0 ) {
-        return(-1);
-    }
-
-    /* Get samples for the actual buffer size */
-    status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
-    if ( status < 0 ) {
-        return(-1);
-    }
-    if ( !override && bufsize != this->spec.samples * 2 ) {
-        return(-1);
-    }
-
-    /* !!! FIXME: Is this safe to do? */
-    this->spec.samples = bufsize / 2;
-
-    /* This is useful for debugging */
-    if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
-        snd_pcm_uframes_t persize = 0;
-        unsigned int periods = 0;
-
-        ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
-        ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
-
-        fprintf(stderr,
-            "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
-            persize, periods, bufsize);
-    }
-
-    return(0);
-}
-
-static int
-ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
+ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params)
 {
-    const char *env;
     int status;
     snd_pcm_hw_params_t *hwparams;
-    snd_pcm_uframes_t frames;
+    snd_pcm_uframes_t persize;
     unsigned int periods;
 
     /* Copy the hardware parameters for this setup */
     snd_pcm_hw_params_alloca(&hwparams);
     ALSA_snd_pcm_hw_params_copy(hwparams, params);
 
-    if ( !override ) {
-        env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
-        if ( env ) {
-            override = SDL_atoi(env);
-            if ( override == 0 ) {
-                return(-1);
-            }
-        }
-    }
-
-    frames = this->spec.samples;
+    /* Attempt to match the period size to the requested buffer size */
+    persize = this->spec.samples;
     status = ALSA_snd_pcm_hw_params_set_period_size_near(
-                this->hidden->pcm_handle, hwparams, &frames, NULL);
+                this->hidden->pcm_handle, hwparams, &persize, NULL);
     if ( status < 0 ) {
         return(-1);
     }
 
+    /* Need to at least double buffer */
     periods = 2;
-    status = ALSA_snd_pcm_hw_params_set_periods_near(
+    status = ALSA_snd_pcm_hw_params_set_periods_min(
                 this->hidden->pcm_handle, hwparams, &periods, NULL);
     if ( status < 0 ) {
         return(-1);
     }
 
-    return ALSA_finalize_hardware(this, hwparams, override);
-}
-
-static int
-ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
-{
-    const char *env;
-    int status;
-    snd_pcm_hw_params_t *hwparams;
-    snd_pcm_uframes_t frames;
-
-    /* Copy the hardware parameters for this setup */
-    snd_pcm_hw_params_alloca(&hwparams);
-    ALSA_snd_pcm_hw_params_copy(hwparams, params);
-
-    if ( !override ) {
-        env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
-        if ( env ) {
-            override = SDL_atoi(env);
-            if ( override == 0 ) {
-                return(-1);
-            }
-        }
+    status = ALSA_snd_pcm_hw_params_set_periods_first(
+                this->hidden->pcm_handle, hwparams, &periods, NULL);
+    if ( status < 0 ) {
+        return(-1);
     }
 
-    frames = this->spec.samples * 2;
-    status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
-                    this->hidden->pcm_handle, hwparams, &frames);
+    /* "set" the hardware with the desired parameters */
+    status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
     if ( status < 0 ) {
         return(-1);
     }
 
-    return ALSA_finalize_hardware(this, hwparams, override);
+    this->spec.samples = persize;
+
+    /* This is useful for debugging */
+    if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
+        snd_pcm_uframes_t bufsize;
+
+        ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
+
+        fprintf(stderr,
+            "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
+            persize, periods, bufsize);
+    }
+
+    return(0);
 }
 
 static int
@@ -692,14 +662,11 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     this->spec.freq = rate;
 
     /* Set the buffer size, in samples */
-    if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
-         ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
-        /* Failed to set desired buffer size, do the best you can... */
-        status = ALSA_set_period_size(this, hwparams, 1);
-        if (status < 0) {
-            return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
-        }
+    status = ALSA_set_buffer_size(this, hwparams);
+    if (status < 0) {
+        return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
     }
+
     /* Set the software parameters */
     snd_pcm_sw_params_alloca(&swparams);
     status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
@@ -737,9 +704,11 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
     }
 
+    #if !SDL_ALSA_NON_BLOCKING
     if (!iscapture) {
         ALSA_snd_pcm_nonblock(pcm_handle, 0);
     }
+    #endif
 
     /* We're ready to rock and roll. :-) */
     return 0;
@@ -828,7 +797,7 @@ ALSA_HotplugThread(void *arg)
         ALSA_Device *seen;
         ALSA_Device *prev;
 
-        if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
+        if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
             int i, j;
             const char *match = NULL;
             int bestmatch = 0xFFFF;

Some files were not shown because too many files changed in this diff