Browse Source

Updates SDL to 2.0.5

Areloch 8 years ago
parent
commit
1e671bfc7a
100 changed files with 5436 additions and 1913 deletions
  1. 10 10
      Engine/lib/sdl/include/SDL.h
  2. 81 14
      Engine/lib/sdl/include/SDL_audio.h
  3. 2 0
      Engine/lib/sdl/include/SDL_config.h.cmake
  4. 4 0
      Engine/lib/sdl/include/SDL_config.h.in
  5. 3 0
      Engine/lib/sdl/include/SDL_config_android.h
  6. 1 5
      Engine/lib/sdl/include/SDL_config_iphoneos.h
  7. 6 2
      Engine/lib/sdl/include/SDL_events.h
  8. 3 3
      Engine/lib/sdl/include/SDL_gamecontroller.h
  9. 16 16
      Engine/lib/sdl/include/SDL_haptic.h
  10. 98 14
      Engine/lib/sdl/include/SDL_hints.h
  11. 1 1
      Engine/lib/sdl/include/SDL_joystick.h
  12. 1 1
      Engine/lib/sdl/include/SDL_keyboard.h
  13. 1 1
      Engine/lib/sdl/include/SDL_main.h
  14. 4 2
      Engine/lib/sdl/include/SDL_mouse.h
  15. 1 0
      Engine/lib/sdl/include/SDL_opengles.h
  16. 2 0
      Engine/lib/sdl/include/SDL_opengles2.h
  17. 14 0
      Engine/lib/sdl/include/SDL_pixels.h
  18. 9 5
      Engine/lib/sdl/include/SDL_platform.h
  19. 26 1
      Engine/lib/sdl/include/SDL_render.h
  20. 2 2
      Engine/lib/sdl/include/SDL_revision.h
  21. 6 6
      Engine/lib/sdl/include/SDL_rwops.h
  22. 7 3
      Engine/lib/sdl/include/SDL_stdinc.h
  23. 10 0
      Engine/lib/sdl/include/SDL_surface.h
  24. 21 1
      Engine/lib/sdl/include/SDL_syswm.h
  25. 1 1
      Engine/lib/sdl/include/SDL_version.h
  26. 124 9
      Engine/lib/sdl/include/SDL_video.h
  27. 12 10
      Engine/lib/sdl/src/SDL.c
  28. 7 0
      Engine/lib/sdl/src/SDL_error.c
  29. 13 0
      Engine/lib/sdl/src/SDL_hints.c
  30. 4 4
      Engine/lib/sdl/src/SDL_log.c
  31. 382 169
      Engine/lib/sdl/src/audio/SDL_audio.c
  32. 0 3
      Engine/lib/sdl/src/audio/SDL_audio_c.h
  33. 49 1
      Engine/lib/sdl/src/audio/SDL_mixer.c
  34. 15 9
      Engine/lib/sdl/src/audio/SDL_sysaudio.h
  35. 1 1
      Engine/lib/sdl/src/audio/SDL_wave.c
  36. 374 76
      Engine/lib/sdl/src/audio/alsa/SDL_alsa_audio.c
  37. 73 36
      Engine/lib/sdl/src/audio/android/SDL_androidaudio.c
  38. 0 2
      Engine/lib/sdl/src/audio/android/SDL_androidaudio.h
  39. 7 26
      Engine/lib/sdl/src/audio/arts/SDL_artsaudio.c
  40. 110 55
      Engine/lib/sdl/src/audio/bsd/SDL_bsdaudio.c
  41. 13 3
      Engine/lib/sdl/src/audio/coreaudio/SDL_coreaudio.h
  42. 849 0
      Engine/lib/sdl/src/audio/coreaudio/SDL_coreaudio.m
  43. 186 98
      Engine/lib/sdl/src/audio/directsound/SDL_directsound.c
  44. 2 1
      Engine/lib/sdl/src/audio/directsound/SDL_directsound.h
  45. 96 63
      Engine/lib/sdl/src/audio/disk/SDL_diskaudio.c
  46. 2 3
      Engine/lib/sdl/src/audio/disk/SDL_diskaudio.h
  47. 43 31
      Engine/lib/sdl/src/audio/dsp/SDL_dspaudio.c
  48. 22 5
      Engine/lib/sdl/src/audio/dummy/SDL_dummyaudio.c
  49. 225 61
      Engine/lib/sdl/src/audio/emscripten/SDL_emscriptenaudio.c
  50. 6 16
      Engine/lib/sdl/src/audio/esd/SDL_esdaudio.c
  51. 11 28
      Engine/lib/sdl/src/audio/fusionsound/SDL_fsaudio.c
  52. 9 15
      Engine/lib/sdl/src/audio/haiku/SDL_haikuaudio.cc
  53. 17 20
      Engine/lib/sdl/src/audio/nacl/SDL_naclaudio.c
  54. 116 50
      Engine/lib/sdl/src/audio/nas/SDL_nasaudio.c
  55. 6 18
      Engine/lib/sdl/src/audio/paudio/SDL_paudio.c
  56. 22 41
      Engine/lib/sdl/src/audio/psp/SDL_pspaudio.c
  57. 132 59
      Engine/lib/sdl/src/audio/pulseaudio/SDL_pulseaudio.c
  58. 3 0
      Engine/lib/sdl/src/audio/pulseaudio/SDL_pulseaudio.h
  59. 27 65
      Engine/lib/sdl/src/audio/qsa/SDL_qsa_audio.c
  60. 1 1
      Engine/lib/sdl/src/audio/qsa/SDL_qsa_audio.h
  61. 7 24
      Engine/lib/sdl/src/audio/sndio/SDL_sndioaudio.c
  62. 7 14
      Engine/lib/sdl/src/audio/sun/SDL_sunaudio.c
  63. 121 63
      Engine/lib/sdl/src/audio/winmm/SDL_winmm.c
  64. 30 54
      Engine/lib/sdl/src/audio/xaudio2/SDL_xaudio2.c
  65. 242 90
      Engine/lib/sdl/src/core/android/SDL_android.c
  66. 4 2
      Engine/lib/sdl/src/core/android/SDL_android.h
  67. 1 1
      Engine/lib/sdl/src/core/linux/SDL_dbus.c
  68. 491 435
      Engine/lib/sdl/src/core/linux/SDL_evdev.c
  69. 0 20
      Engine/lib/sdl/src/core/linux/SDL_evdev.h
  70. 553 0
      Engine/lib/sdl/src/core/linux/SDL_fcitx.c
  71. 40 0
      Engine/lib/sdl/src/core/linux/SDL_fcitx.h
  72. 4 2
      Engine/lib/sdl/src/core/linux/SDL_ibus.c
  73. 138 0
      Engine/lib/sdl/src/core/linux/SDL_ime.c
  74. 38 0
      Engine/lib/sdl/src/core/linux/SDL_ime.h
  75. 8 1
      Engine/lib/sdl/src/core/linux/SDL_udev.c
  76. 4 2
      Engine/lib/sdl/src/core/linux/SDL_udev.h
  77. 78 0
      Engine/lib/sdl/src/core/windows/SDL_windows.c
  78. 3 0
      Engine/lib/sdl/src/core/windows/SDL_windows.h
  79. 26 5
      Engine/lib/sdl/src/core/winrt/SDL_winrtapp_direct3d.cpp
  80. 4 0
      Engine/lib/sdl/src/core/winrt/SDL_winrtapp_direct3d.h
  81. 1 1
      Engine/lib/sdl/src/dynapi/SDL_dynapi.c
  82. 8 2
      Engine/lib/sdl/src/dynapi/SDL_dynapi.h
  83. 15 0
      Engine/lib/sdl/src/dynapi/SDL_dynapi_overrides.h
  84. 13 0
      Engine/lib/sdl/src/dynapi/SDL_dynapi_procs.h
  85. 60 8
      Engine/lib/sdl/src/events/SDL_dropevents.c
  86. 3 1
      Engine/lib/sdl/src/events/SDL_dropevents_c.h
  87. 59 58
      Engine/lib/sdl/src/events/SDL_events.c
  88. 1 1
      Engine/lib/sdl/src/events/SDL_gesture.c
  89. 37 33
      Engine/lib/sdl/src/events/SDL_mouse.c
  90. 3 0
      Engine/lib/sdl/src/events/SDL_mouse_c.h
  91. 1 3
      Engine/lib/sdl/src/events/SDL_quit.c
  92. 17 1
      Engine/lib/sdl/src/events/SDL_windowevents.c
  93. 7 7
      Engine/lib/sdl/src/events/scancodes_linux.h
  94. 85 0
      Engine/lib/sdl/src/events/scancodes_xfree86.h
  95. 2 2
      Engine/lib/sdl/src/filesystem/dummy/SDL_sysfilesystem.c
  96. 21 2
      Engine/lib/sdl/src/filesystem/unix/SDL_sysfilesystem.c
  97. 2 2
      Engine/lib/sdl/src/haptic/linux/SDL_syshaptic.c
  98. 10 9
      Engine/lib/sdl/src/haptic/windows/SDL_dinputhaptic.c
  99. 1 1
      Engine/lib/sdl/src/haptic/windows/SDL_windowshaptic.c
  100. 2 2
      Engine/lib/sdl/src/haptic/windows/SDL_windowshaptic_c.h

+ 10 - 10
Engine/lib/sdl/include/SDL.h

@@ -72,14 +72,14 @@ extern "C" {
  *  specify the subsystems which you will be using in your application.
  */
 /* @{ */
-#define SDL_INIT_TIMER          0x00000001
-#define SDL_INIT_AUDIO          0x00000010
-#define SDL_INIT_VIDEO          0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
-#define SDL_INIT_JOYSTICK       0x00000200  /**< SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS */
-#define SDL_INIT_HAPTIC         0x00001000
-#define SDL_INIT_GAMECONTROLLER 0x00002000  /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
-#define SDL_INIT_EVENTS         0x00004000
-#define SDL_INIT_NOPARACHUTE    0x00100000  /**< compatibility; this flag is ignored. */
+#define SDL_INIT_TIMER          0x00000001u
+#define SDL_INIT_AUDIO          0x00000010u
+#define SDL_INIT_VIDEO          0x00000020u  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
+#define SDL_INIT_JOYSTICK       0x00000200u  /**< SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS */
+#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_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 \
@@ -95,8 +95,8 @@ extern DECLSPEC int SDLCALL SDL_Init(Uint32 flags);
  *  This function initializes specific SDL subsystems
  *
  *  Subsystem initialization is ref-counted, you must call
- *  SDL_QuitSubSystem for each SDL_InitSubSystem to correctly
- *  shutdown a subsystem manually (or call SDL_Quit to force shutdown).
+ *  SDL_QuitSubSystem() for each SDL_InitSubSystem() to correctly
+ *  shutdown a subsystem manually (or call SDL_Quit() to force shutdown).
  *  If a subsystem is already loaded then this call will
  *  increase the ref-count and return.
  */

+ 81 - 14
Engine/lib/sdl/include/SDL_audio.h

@@ -278,7 +278,8 @@ extern DECLSPEC const char *SDLCALL SDL_GetCurrentAudioDriver(void);
  *      protect data structures that it accesses by calling SDL_LockAudio()
  *      and SDL_UnlockAudio() in your code. Alternately, you may pass a NULL
  *      pointer here, and call SDL_QueueAudio() with some frequency, to queue
- *      more audio samples to be played.
+ *      more audio samples to be played (or for capture devices, call
+ *      SDL_DequeueAudio() with some frequency, to obtain audio samples).
  *    - \c desired->userdata is passed as the first parameter to your callback
  *      function. If you passed a NULL callback, this value is ignored.
  *
@@ -482,6 +483,10 @@ extern DECLSPEC void SDLCALL SDL_MixAudioFormat(Uint8 * dst,
 /**
  *  Queue more audio on non-callback devices.
  *
+ *  (If you are looking to retrieve queued audio from a non-callback capture
+ *  device, you want SDL_DequeueAudio() instead. This will return -1 to
+ *  signify an error if you use it with capture devices.)
+ *
  *  SDL offers two ways to feed audio to the device: you can either supply a
  *  callback that SDL triggers with some frequency to obtain more audio
  *  (pull method), or you can supply no callback, and then SDL will expect
@@ -516,21 +521,76 @@ extern DECLSPEC void SDLCALL SDL_MixAudioFormat(Uint8 * dst,
  */
 extern DECLSPEC int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID dev, const void *data, Uint32 len);
 
+/**
+ *  Dequeue more audio on non-callback devices.
+ *
+ *  (If you are looking to queue audio for output on a non-callback playback
+ *  device, you want SDL_QueueAudio() instead. This will always return 0
+ *  if you use it with playback devices.)
+ *
+ *  SDL offers two ways to retrieve audio from a capture device: you can
+ *  either supply a callback that SDL triggers with some frequency as the
+ *  device records more audio data, (push method), or you can supply no
+ *  callback, and then SDL will expect you to retrieve data at regular
+ *  intervals (pull method) with this function.
+ *
+ *  There are no limits on the amount of data you can queue, short of
+ *  exhaustion of address space. Data from the device will keep queuing as
+ *  necessary without further intervention from you. This means you will
+ *  eventually run out of memory if you aren't routinely dequeueing data.
+ *
+ *  Capture devices will not queue data when paused; if you are expecting
+ *  to not need captured audio for some length of time, use
+ *  SDL_PauseAudioDevice() to stop the capture device from queueing more
+ *  data. This can be useful during, say, level loading times. When
+ *  unpaused, capture devices will start queueing data from that point,
+ *  having flushed any capturable data available while paused.
+ *
+ *  This function is thread-safe, but dequeueing from the same device from
+ *  two threads at once does not promise which thread will dequeued data
+ *  first.
+ *
+ *  You may not dequeue audio from a device that is using an
+ *  application-supplied callback; doing so returns an error. You have to use
+ *  the audio callback, or dequeue audio with this function, but not both.
+ *
+ *  You should not call SDL_LockAudio() on the device before queueing; SDL
+ *  handles locking internally for this function.
+ *
+ *  \param dev The device ID from which we will dequeue audio.
+ *  \param data A pointer into where audio data should be copied.
+ *  \param len The number of bytes (not samples!) to which (data) points.
+ *  \return number of bytes dequeued, which could be less than requested.
+ *
+ *  \sa SDL_GetQueuedAudioSize
+ *  \sa SDL_ClearQueuedAudio
+ */
+extern DECLSPEC Uint32 SDLCALL SDL_DequeueAudio(SDL_AudioDeviceID dev, void *data, Uint32 len);
+
 /**
  *  Get the number of bytes of still-queued audio.
  *
- *  This is the number of bytes that have been queued for playback with
- *  SDL_QueueAudio(), but have not yet been sent to the hardware.
+ *  For playback device:
+ *
+ *    This is the number of bytes that have been queued for playback with
+ *    SDL_QueueAudio(), but have not yet been sent to the hardware. This
+ *    number may shrink at any time, so this only informs of pending data.
+ *
+ *    Once we've sent it to the hardware, this function can not decide the
+ *    exact byte boundary of what has been played. It's possible that we just
+ *    gave the hardware several kilobytes right before you called this
+ *    function, but it hasn't played any of it yet, or maybe half of it, etc.
+ *
+ *  For capture devices:
  *
- *  Once we've sent it to the hardware, this function can not decide the exact
- *  byte boundary of what has been played. It's possible that we just gave the
- *  hardware several kilobytes right before you called this function, but it
- *  hasn't played any of it yet, or maybe half of it, etc.
+ *    This is the number of bytes that have been captured by the device and
+ *    are waiting for you to dequeue. This number may grow at any time, so
+ *    this only informs of the lower-bound of available data.
  *
  *  You may not queue audio on a device that is using an application-supplied
  *  callback; calling this function on such a device always returns 0.
- *  You have to use the audio callback or queue audio with SDL_QueueAudio(),
- *  but not both.
+ *  You have to queue audio with SDL_QueueAudio()/SDL_DequeueAudio(), or use
+ *  the audio callback, but not both.
  *
  *  You should not call SDL_LockAudio() on the device before querying; SDL
  *  handles locking internally for this function.
@@ -544,10 +604,17 @@ extern DECLSPEC int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID dev, const void *da
 extern DECLSPEC Uint32 SDLCALL SDL_GetQueuedAudioSize(SDL_AudioDeviceID dev);
 
 /**
- *  Drop any queued audio data waiting to be sent to the hardware.
+ *  Drop any queued audio data. For playback devices, this is any queued data
+ *  still waiting to be submitted to the hardware. For capture devices, this
+ *  is any data that was queued by the device that hasn't yet been dequeued by
+ *  the application.
  *
- *  Immediately after this call, SDL_GetQueuedAudioSize() will return 0 and
- *  the hardware will start playing silence if more audio isn't queued.
+ *  Immediately after this call, SDL_GetQueuedAudioSize() will return 0. For
+ *  playback devices, the hardware will start playing silence if more audio
+ *  isn't queued. Unpaused capture devices will start filling the queue again
+ *  as soon as they have more data available (which, depending on the state
+ *  of the hardware and the thread, could be before this function call
+ *  returns!).
  *
  *  This will not prevent playback of queued audio that's already been sent
  *  to the hardware, as we can not undo that, so expect there to be some
@@ -557,8 +624,8 @@ extern DECLSPEC Uint32 SDLCALL SDL_GetQueuedAudioSize(SDL_AudioDeviceID dev);
  *
  *  You may not queue audio on a device that is using an application-supplied
  *  callback; calling this function on such a device is always a no-op.
- *  You have to use the audio callback or queue audio with SDL_QueueAudio(),
- *  but not both.
+ *  You have to queue audio with SDL_QueueAudio()/SDL_DequeueAudio(), or use
+ *  the audio callback, but not both.
  *
  *  You should not call SDL_LockAudio() on the device before clearing the
  *  queue; SDL handles locking internally for this function.

+ 2 - 0
Engine/lib/sdl/include/SDL_config.h.cmake

@@ -81,6 +81,8 @@
 #cmakedefine HAVE_PTHREAD_NP_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
 
 /* C library functions */
 #cmakedefine HAVE_MALLOC 1

+ 4 - 0
Engine/lib/sdl/include/SDL_config.h.in

@@ -82,6 +82,7 @@
 #undef HAVE_LIBUDEV_H
 #undef HAVE_DBUS_DBUS_H
 #undef HAVE_IBUS_IBUS_H
+#undef HAVE_FCITX_FRONTEND_H
 
 /* C library functions */
 #undef HAVE_MALLOC
@@ -356,4 +357,7 @@
 #undef SDL_ASSEMBLY_ROUTINES
 #undef SDL_ALTIVEC_BLITTERS
 
+/* Enable ime support */
+#undef SDL_USE_IME
+
 #endif /* _SDL_config_h */

+ 3 - 0
Engine/lib/sdl/include/SDL_config_android.h

@@ -43,6 +43,7 @@
 #define HAVE_STDINT_H   1
 #define HAVE_CTYPE_H    1
 #define HAVE_MATH_H 1
+#define HAVE_SIGNAL_H 1
 
 /* C library functions */
 #define HAVE_MALLOC 1
@@ -75,6 +76,7 @@
 #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
@@ -101,6 +103,7 @@
 #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

+ 1 - 5
Engine/lib/sdl/include/SDL_config_iphoneos.h

@@ -119,11 +119,7 @@
 #define SDL_JOYSTICK_MFI 1
 
 /* Enable Unix style SO loading */
-/* Technically this works, but violates the iOS dev agreement prior to iOS 8 */
-/* #define SDL_LOADSO_DLOPEN 1 */
-
-/* Enable the stub shared object loader (src/loadso/dummy/\*.c) */
-#define SDL_LOADSO_DISABLED 1
+#define SDL_LOADSO_DLOPEN 1
 
 /* Enable various threading systems */
 #define SDL_THREAD_PTHREAD  1

+ 6 - 2
Engine/lib/sdl/include/SDL_events.h

@@ -136,6 +136,9 @@ typedef enum
 
     /* Drag and drop events */
     SDL_DROPFILE        = 0x1000, /**< The system requests a file open */
+    SDL_DROPTEXT,                 /**< text/plain drag-and-drop event */
+    SDL_DROPBEGIN,                /**< A new set of drops is beginning (NULL filename) */
+    SDL_DROPCOMPLETE,             /**< Current set of drops is now complete (NULL filename) */
 
     /* Audio hotplug events */
     SDL_AUDIODEVICEADDED = 0x1100, /**< A new audio device is available */
@@ -461,9 +464,10 @@ typedef struct SDL_DollarGestureEvent
  */
 typedef struct SDL_DropEvent
 {
-    Uint32 type;        /**< ::SDL_DROPFILE */
+    Uint32 type;        /**< ::SDL_DROPBEGIN or ::SDL_DROPFILE or ::SDL_DROPTEXT or ::SDL_DROPCOMPLETE */
     Uint32 timestamp;
-    char *file;         /**< The file name, which should be freed with SDL_free() */
+    char *file;         /**< The file name, which should be freed with SDL_free(), is NULL on begin/complete */
+    Uint32 windowID;    /**< The window that was dropped on, if any */
 } SDL_DropEvent;
 
 

+ 3 - 3
Engine/lib/sdl/include/SDL_gamecontroller.h

@@ -93,7 +93,7 @@ typedef struct SDL_GameControllerButtonBind
  *      }
  *  }
  *
- *  Using the SDL_HINT_GAMECONTROLLERCONFIG hint or the SDL_GameControllerAddMapping you can add support for controllers SDL is unaware of or cause an existing controller to have a different binding. The format is:
+ *  Using the SDL_HINT_GAMECONTROLLERCONFIG hint or the SDL_GameControllerAddMapping() you can add support for controllers SDL is unaware of or cause an existing controller to have a different binding. The format is:
  *  guid,name,mappings
  *
  *  Where GUID is the string value from SDL_JoystickGetGUIDString(), name is the human readable string for the device and mappings are controller mappings to joystick ones.
@@ -136,14 +136,14 @@ extern DECLSPEC int SDLCALL SDL_GameControllerAddMapping( const char* mappingStr
 /**
  *  Get a mapping string for a GUID
  *
- *  \return the mapping string.  Must be freed with SDL_free.  Returns NULL if no mapping is available
+ *  \return the mapping string.  Must be freed with SDL_free().  Returns NULL if no mapping is available
  */
 extern DECLSPEC char * SDLCALL SDL_GameControllerMappingForGUID( SDL_JoystickGUID guid );
 
 /**
  *  Get a mapping string for an open GameController
  *
- *  \return the mapping string.  Must be freed with SDL_free.  Returns NULL if no mapping is available
+ *  \return the mapping string.  Must be freed with SDL_free().  Returns NULL if no mapping is available
  */
 extern DECLSPEC char * SDLCALL SDL_GameControllerMapping( SDL_GameController * gamecontroller );
 

+ 16 - 16
Engine/lib/sdl/include/SDL_haptic.h

@@ -149,7 +149,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticCondition
  */
-#define SDL_HAPTIC_CONSTANT   (1<<0)
+#define SDL_HAPTIC_CONSTANT   (1u<<0)
 
 /**
  *  \brief Sine wave effect supported.
@@ -158,7 +158,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticPeriodic
  */
-#define SDL_HAPTIC_SINE       (1<<1)
+#define SDL_HAPTIC_SINE       (1u<<1)
 
 /**
  *  \brief Left/Right effect supported.
@@ -169,7 +169,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  * \warning this value was SDL_HAPTIC_SQUARE right before 2.0.0 shipped. Sorry,
  *          we ran out of bits, and this is important for XInput devices.
  */
-#define SDL_HAPTIC_LEFTRIGHT     (1<<2)
+#define SDL_HAPTIC_LEFTRIGHT     (1u<<2)
 
 /* !!! FIXME: put this back when we have more bits in 2.1 */
 /* #define SDL_HAPTIC_SQUARE     (1<<2) */
@@ -181,7 +181,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticPeriodic
  */
-#define SDL_HAPTIC_TRIANGLE   (1<<3)
+#define SDL_HAPTIC_TRIANGLE   (1u<<3)
 
 /**
  *  \brief Sawtoothup wave effect supported.
@@ -190,7 +190,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticPeriodic
  */
-#define SDL_HAPTIC_SAWTOOTHUP (1<<4)
+#define SDL_HAPTIC_SAWTOOTHUP (1u<<4)
 
 /**
  *  \brief Sawtoothdown wave effect supported.
@@ -199,7 +199,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticPeriodic
  */
-#define SDL_HAPTIC_SAWTOOTHDOWN (1<<5)
+#define SDL_HAPTIC_SAWTOOTHDOWN (1u<<5)
 
 /**
  *  \brief Ramp effect supported.
@@ -208,7 +208,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticRamp
  */
-#define SDL_HAPTIC_RAMP       (1<<6)
+#define SDL_HAPTIC_RAMP       (1u<<6)
 
 /**
  *  \brief Spring effect supported - uses axes position.
@@ -218,7 +218,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticCondition
  */
-#define SDL_HAPTIC_SPRING     (1<<7)
+#define SDL_HAPTIC_SPRING     (1u<<7)
 
 /**
  *  \brief Damper effect supported - uses axes velocity.
@@ -228,7 +228,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticCondition
  */
-#define SDL_HAPTIC_DAMPER     (1<<8)
+#define SDL_HAPTIC_DAMPER     (1u<<8)
 
 /**
  *  \brief Inertia effect supported - uses axes acceleration.
@@ -238,7 +238,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticCondition
  */
-#define SDL_HAPTIC_INERTIA    (1<<9)
+#define SDL_HAPTIC_INERTIA    (1u<<9)
 
 /**
  *  \brief Friction effect supported - uses axes movement.
@@ -248,14 +248,14 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticCondition
  */
-#define SDL_HAPTIC_FRICTION   (1<<10)
+#define SDL_HAPTIC_FRICTION   (1u<<10)
 
 /**
  *  \brief Custom effect is supported.
  *
  *  User defined custom haptic effect.
  */
-#define SDL_HAPTIC_CUSTOM     (1<<11)
+#define SDL_HAPTIC_CUSTOM     (1u<<11)
 
 /* @} *//* Haptic effects */
 
@@ -268,7 +268,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticSetGain
  */
-#define SDL_HAPTIC_GAIN       (1<<12)
+#define SDL_HAPTIC_GAIN       (1u<<12)
 
 /**
  *  \brief Device can set autocenter.
@@ -277,7 +277,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticSetAutocenter
  */
-#define SDL_HAPTIC_AUTOCENTER (1<<13)
+#define SDL_HAPTIC_AUTOCENTER (1u<<13)
 
 /**
  *  \brief Device can be queried for effect status.
@@ -286,7 +286,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *
  *  \sa SDL_HapticGetEffectStatus
  */
-#define SDL_HAPTIC_STATUS     (1<<14)
+#define SDL_HAPTIC_STATUS     (1u<<14)
 
 /**
  *  \brief Device can be paused.
@@ -294,7 +294,7 @@ typedef struct _SDL_Haptic SDL_Haptic;
  *  \sa SDL_HapticPause
  *  \sa SDL_HapticUnpause
  */
-#define SDL_HAPTIC_PAUSE      (1<<15)
+#define SDL_HAPTIC_PAUSE      (1u<<15)
 
 
 /**

+ 98 - 14
Engine/lib/sdl/include/SDL_hints.h

@@ -233,16 +233,27 @@ extern "C" {
 #define SDL_HINT_GRAB_KEYBOARD              "SDL_GRAB_KEYBOARD"
 
 /**
-*  \brief  A variable controlling whether relative mouse mode is implemented using mouse warping
-*
-*  This variable can be set to the following values:
-*    "0"       - Relative mouse mode uses raw input
-*    "1"       - Relative mouse mode uses mouse warping
-*
-*  By default SDL will use raw input for relative mouse mode
-*/
+ *  \brief  A variable controlling whether relative mouse mode is implemented using mouse warping
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Relative mouse mode uses raw input
+ *    "1"       - Relative mouse mode uses mouse warping
+ *
+ *  By default SDL will use raw input for relative mouse mode
+ */
 #define SDL_HINT_MOUSE_RELATIVE_MODE_WARP    "SDL_MOUSE_RELATIVE_MODE_WARP"
 
+/**
+ *  \brief Allow mouse click events when clicking to focus an SDL window
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Ignore mouse clicks that activate a window
+ *    "1"       - Generate events for mouse clicks that activate a window
+ *
+ *  By default SDL will ignore mouse clicks that activate a window
+ */
+#define SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH "SDL_MOUSE_FOCUS_CLICKTHROUGH"
+
 /**
  *  \brief Minimize your SDL_Window if it loses key focus when in fullscreen mode. Defaults to true.
  *
@@ -257,8 +268,8 @@ extern "C" {
  *  this is problematic. This functionality can be disabled by setting this
  *  hint.
  *
- *  As of SDL 2.0.4, SDL_EnableScreenSaver and SDL_DisableScreenSaver accomplish
- *  the same thing on iOS. They should be preferred over this hint.
+ *  As of SDL 2.0.4, SDL_EnableScreenSaver() and SDL_DisableScreenSaver()
+ *  accomplish the same thing on iOS. They should be preferred over this hint.
  *
  *  This variable can be set to the following values:
  *    "0"       - Enable idle timer
@@ -276,7 +287,35 @@ extern "C" {
  *    "LandscapeLeft", "LandscapeRight", "Portrait" "PortraitUpsideDown"
  */
 #define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS"
-    
+
+/**
+ *  \brief  A variable controlling whether controllers used with the Apple TV
+ *  generate UI events.
+ *
+ * When UI events are generated by controller input, the app will be
+ * backgrounded when the Apple TV remote's menu button is pressed, and when the
+ * pause or B buttons on gamepads are pressed.
+ *
+ * More information about properly making use of controllers for the Apple TV
+ * can be found here:
+ * https://developer.apple.com/tvos/human-interface-guidelines/remote-and-controllers/
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Controller input does not generate UI events (the default).
+ *    "1"       - Controller input generates UI events.
+ */
+#define SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS "SDL_APPLE_TV_CONTROLLER_UI_EVENTS"
+
+/**
+ * \brief  A variable controlling whether the Apple TV remote's joystick axes
+ *         will automatically match the rotation of the remote.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - Remote orientation does not affect joystick axes (the default).
+ *    "1"       - Joystick axes are based on the orientation of the remote.
+ */
+#define SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION"
+
 /**
  *  \brief  A variable controlling whether the Android / iOS built-in
  *  accelerometer should be listed as a joystick device, rather than listing
@@ -369,7 +408,7 @@ extern "C" {
 *  Use this hint in case you need to set SDL's threads stack size to other than the default.
 *  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 backend.
+*  Support for this hint is currently available only in the pthread, Windows, and PSP backend.
 */
 #define SDL_HINT_THREAD_STACK_SIZE              "SDL_THREAD_STACK_SIZE"
 
@@ -431,7 +470,7 @@ extern "C" {
  *  privacy policy.
  *
  *  To setup a URL to an app's privacy policy, set SDL_HINT_WINRT_PRIVACY_POLICY_URL
- *  before calling any SDL_Init functions.  The contents of the hint should
+ *  before calling any SDL_Init() functions.  The contents of the hint should
  *  be a valid URL.  For example, "http://www.example.com".
  *
  *  The default value is "", which will prevent SDL from adding a privacy policy
@@ -461,7 +500,7 @@ extern "C" {
  *  The contents of this hint should be encoded as a UTF8 string.
  *
  *  The default value is "Privacy Policy".  This hint should only be set during app
- *  initialization, preferably before any calls to SDL_Init.
+ *  initialization, preferably before any calls to SDL_Init().
  *
  *  For additional information on linking to a privacy policy, see the documentation for
  *  SDL_HINT_WINRT_PRIVACY_POLICY_URL.
@@ -630,6 +669,44 @@ extern "C" {
  */
 #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.
+ *
+ * The bitmap header version 4 is required for proper alpha channel support and
+ * SDL will use it when required. Should this not be desired, this hint can
+ * force the use of the 40 byte header version which is supported everywhere.
+ *
+ * The variable can be set to the following values:
+ *   "0"       - Surfaces with a colorkey or an alpha channel are saved to a
+ *               32-bit BMP file with an alpha mask. SDL will use the bitmap
+ *               header version 4 and set the alpha mask accordingly.
+ *   "1"       - Surfaces with a colorkey or an alpha channel are saved to a
+ *               32-bit BMP file without an alpha mask. The alpha channel data
+ *               will be in the file, but applications are going to ignore it.
+ *
+ * The default value is "0".
+ */
+#define SDL_HINT_BMP_SAVE_LEGACY_FORMAT "SDL_BMP_SAVE_LEGACY_FORMAT"
+
+/**
+ * \brief Tell SDL not to name threads on Windows.
+ *
+ * The variable can be set to the following values:
+ *   "0"       - SDL will raise the 0x406D1388 Exception to name threads.
+ *               This is the default behavior of SDL <= 2.0.4. (default)
+ *   "1"       - SDL will not raise this exception, and threads will be unnamed.
+ *               For .NET languages this is required when running under a debugger.
+ */
+#define SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING "SDL_WINDOWS_DISABLE_THREAD_NAMING"
+
+/**
+ * \brief Tell SDL which Dispmanx layer to use on a Raspberry PI
+ *
+ * Also known as Z-order. The variable can take a negative or positive value.
+ * The default is 10000.
+ */
+#define SDL_HINT_RPI_VIDEO_LAYER           "SDL_RPI_VIDEO_LAYER"
+
 /**
  *  \brief  An enumeration of hint priorities
  */
@@ -669,6 +746,13 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetHint(const char *name,
  */
 extern DECLSPEC const char * SDLCALL SDL_GetHint(const char *name);
 
+/**
+ *  \brief Get a hint
+ *
+ *  \return The boolean value of a hint variable.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_GetHintBoolean(const char *name, SDL_bool default_value);
+
 /**
  *  \brief Add a function to watch a particular hint
  *

+ 1 - 1
Engine/lib/sdl/include/SDL_joystick.h

@@ -24,7 +24,7 @@
  *
  *  Include file for SDL joystick event handling
  *
- * The term "device_index" identifies currently plugged in joystick devices between 0 and SDL_NumJoysticks, with the exact joystick
+ * The term "device_index" identifies currently plugged in joystick devices between 0 and SDL_NumJoysticks(), with the exact joystick
  *   behind a device_index changing as joysticks are plugged and unplugged.
  *
  * The term "instance_id" is the current instantiation of a joystick device in the system, if the joystick is removed and then re-inserted

+ 1 - 1
Engine/lib/sdl/include/SDL_keyboard.h

@@ -136,7 +136,7 @@ extern DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromName(const char *name);
  *          copy it.  If the key doesn't have a name, this function returns an
  *          empty string ("").
  *
- *  \sa SDL_Key
+ *  \sa SDL_Keycode
  */
 extern DECLSPEC const char *SDLCALL SDL_GetKeyName(SDL_Keycode key);
 

+ 1 - 1
Engine/lib/sdl/include/SDL_main.h

@@ -63,7 +63,7 @@
 /* On Android SDL provides a Java class in SDLActivity.java that is the
    main activity entry point.
 
-   See README-android.txt for more details on extending that class.
+   See README-android.md for more details on extending that class.
  */
 #define SDL_MAIN_NEEDED
 

+ 4 - 2
Engine/lib/sdl/include/SDL_mouse.h

@@ -41,7 +41,7 @@ extern "C" {
 typedef struct SDL_Cursor SDL_Cursor;   /* Implementation dependent */
 
 /**
- * \brief Cursor types for SDL_CreateSystemCursor.
+ * \brief Cursor types for SDL_CreateSystemCursor().
  */
 typedef enum
 {
@@ -254,9 +254,11 @@ extern DECLSPEC SDL_Cursor *SDLCALL SDL_GetCursor(void);
 extern DECLSPEC SDL_Cursor *SDLCALL SDL_GetDefaultCursor(void);
 
 /**
- *  \brief Frees a cursor created with SDL_CreateCursor().
+ *  \brief Frees a cursor created with SDL_CreateCursor() or similar functions.
  *
  *  \sa SDL_CreateCursor()
+ *  \sa SDL_CreateColorCursor()
+ *  \sa SDL_CreateSystemCursor()
  */
 extern DECLSPEC void SDLCALL SDL_FreeCursor(SDL_Cursor * cursor);
 

+ 1 - 0
Engine/lib/sdl/include/SDL_opengles.h

@@ -24,6 +24,7 @@
  *
  *  This is a simple file to encapsulate the OpenGL ES 1.X API headers.
  */
+#include "SDL_config.h"
 
 #ifdef __IPHONEOS__
 #include <OpenGLES/ES1/gl.h>

+ 2 - 0
Engine/lib/sdl/include/SDL_opengles2.h

@@ -24,6 +24,8 @@
  *
  *  This is a simple file to encapsulate the OpenGL ES 2.0 API headers.
  */
+#include "SDL_config.h"
+
 #ifndef _MSC_VER
 
 #ifdef __IPHONEOS__

+ 14 - 0
Engine/lib/sdl/include/SDL_pixels.h

@@ -29,6 +29,7 @@
 #define _SDL_pixels_h
 
 #include "SDL_stdinc.h"
+#include "SDL_endian.h"
 
 #include "begin_code.h"
 /* Set up for C function definitions, even when using C++ */
@@ -260,6 +261,19 @@ enum
         SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
                                SDL_PACKEDLAYOUT_2101010, 32, 4),
 
+    /* Aliases for RGBA byte arrays of color data, for the current platform */
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+    SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_RGBA8888,
+    SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_ARGB8888,
+    SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_BGRA8888,
+    SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_ABGR8888,
+#else
+    SDL_PIXELFORMAT_RGBA32 = SDL_PIXELFORMAT_ABGR8888,
+    SDL_PIXELFORMAT_ARGB32 = SDL_PIXELFORMAT_BGRA8888,
+    SDL_PIXELFORMAT_BGRA32 = SDL_PIXELFORMAT_ARGB8888,
+    SDL_PIXELFORMAT_ABGR32 = SDL_PIXELFORMAT_RGBA8888,
+#endif
+
     SDL_PIXELFORMAT_YV12 =      /**< Planar mode: Y + V + U  (3 planes) */
         SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'),
     SDL_PIXELFORMAT_IYUV =      /**< Planar mode: Y + U + V  (3 planes) */

+ 9 - 5
Engine/lib/sdl/include/SDL_platform.h

@@ -70,18 +70,22 @@
 /* lets us know what version of Mac OS X we're compiling on */
 #include "AvailabilityMacros.h"
 #include "TargetConditionals.h"
+#if TARGET_OS_TV
+#undef __TVOS__
+#define __TVOS__ 1
+#endif
 #if TARGET_OS_IPHONE
-/* if compiling for iPhone */
+/* if compiling for iOS */
 #undef __IPHONEOS__
 #define __IPHONEOS__ 1
 #undef __MACOSX__
 #else
-/* if not compiling for iPhone */
+/* if not compiling for iOS */
 #undef __MACOSX__
 #define __MACOSX__  1
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
-# error SDL for Mac OS X only supports deploying on 10.5 and above.
-#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1050 */
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+# error SDL for Mac OS X only supports deploying on 10.6 and above.
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */
 #endif /* TARGET_OS_IPHONE */
 #endif /* defined(__APPLE__) */
 

+ 26 - 1
Engine/lib/sdl/include/SDL_render.h

@@ -499,6 +499,30 @@ extern DECLSPEC int SDLCALL SDL_RenderSetLogicalSize(SDL_Renderer * renderer, in
  */
 extern DECLSPEC void SDLCALL SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h);
 
+/**
+ *  \brief Set whether to force integer scales for resolution-independent rendering
+ *
+ *  \param renderer The renderer for which integer scaling should be set.
+ *  \param enable   Enable or disable integer scaling
+ *
+ *  This function restricts the logical viewport to integer values - that is, when
+ *  a resolution is between two multiples of a logical size, the viewport size is
+ *  rounded down to the lower multiple.
+ *
+ *  \sa SDL_RenderSetLogicalSize()
+ */
+extern DECLSPEC int SDLCALL SDL_RenderSetIntegerScale(SDL_Renderer * renderer,
+                                                      SDL_bool enable);
+
+/**
+ *  \brief Get whether integer scales are forced for resolution-independent rendering
+ *
+ *  \param renderer The renderer from which integer scaling should be queried.
+ *
+ *  \sa SDL_RenderSetIntegerScale()
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_RenderGetIntegerScale(SDL_Renderer * renderer);
+
 /**
  *  \brief Set the drawing area for rendering on the current target.
  *
@@ -658,7 +682,8 @@ extern DECLSPEC int SDLCALL SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer,
 /**
  *  \brief Clear the current rendering target with the drawing color
  *
- *  This function clears the entire rendering target, ignoring the viewport.
+ *  This function clears the entire rendering target, ignoring the viewport and
+ *  the clip rectangle.
  *
  *  \return 0 on success, or -1 on error
  */

+ 2 - 2
Engine/lib/sdl/include/SDL_revision.h

@@ -1,2 +1,2 @@
-#define SDL_REVISION "hg-10001:e12c38730512"
-#define SDL_REVISION_NUMBER 10001
+#define SDL_REVISION "hg-10556:007dfe83abf8"
+#define SDL_REVISION_NUMBER 10556

+ 6 - 6
Engine/lib/sdl/include/SDL_rwops.h

@@ -39,12 +39,12 @@ extern "C" {
 #endif
 
 /* RWops Types */
-#define SDL_RWOPS_UNKNOWN   0   /* Unknown stream type */
-#define SDL_RWOPS_WINFILE   1   /* Win32 file */
-#define SDL_RWOPS_STDFILE   2   /* Stdio file */
-#define SDL_RWOPS_JNIFILE   3   /* Android asset */
-#define SDL_RWOPS_MEMORY    4   /* Memory stream */
-#define SDL_RWOPS_MEMORY_RO 5   /* Read-Only memory stream */
+#define SDL_RWOPS_UNKNOWN   0U  /* Unknown stream type */
+#define SDL_RWOPS_WINFILE   1U  /* Win32 file */
+#define SDL_RWOPS_STDFILE   2U  /* Stdio file */
+#define SDL_RWOPS_JNIFILE   3U  /* Android asset */
+#define SDL_RWOPS_MEMORY    4U  /* Memory stream */
+#define SDL_RWOPS_MEMORY_RO 5U  /* Read-Only memory stream */
 
 /**
  * This is the read/write operation structure -- very basic.

+ 7 - 3
Engine/lib/sdl/include/SDL_stdinc.h

@@ -83,9 +83,6 @@
 #ifdef HAVE_FLOAT_H
 # include <float.h>
 #endif
-#if defined(HAVE_ICONV) && defined(HAVE_ICONV_H)
-# include <iconv.h>
-#endif
 
 /**
  *  The number of elements in an array.
@@ -93,6 +90,13 @@
 #define SDL_arraysize(array)    (sizeof(array)/sizeof(array[0]))
 #define SDL_TABLESIZE(table)    SDL_arraysize(table)
 
+/**
+ *  Macro useful for building other macros with strings in them
+ *
+ *  e.g. #define LOG_ERROR(X) OutputDebugString(SDL_STRINGIFY_ARG(__FUNCTION__) ": " X "\n")
+ */
+#define SDL_STRINGIFY_ARG(arg)  #arg
+
 /**
  *  \name Cast operators
  *

+ 10 - 0
Engine/lib/sdl/include/SDL_surface.h

@@ -118,6 +118,8 @@ typedef int (*SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect,
 extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurface
     (Uint32 flags, int width, int height, int depth,
      Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);
+extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceWithFormat
+    (Uint32 flags, int width, int height, int depth, Uint32 format);
 extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels,
                                                               int width,
                                                               int height,
@@ -127,6 +129,8 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels,
                                                               Uint32 Gmask,
                                                               Uint32 Bmask,
                                                               Uint32 Amask);
+extern DECLSPEC SDL_Surface *SDLCALL SDL_CreateRGBSurfaceWithFormatFrom
+    (void *pixels, int width, int height, int depth, int pitch, Uint32 format);
 extern DECLSPEC void SDLCALL SDL_FreeSurface(SDL_Surface * surface);
 
 /**
@@ -184,6 +188,12 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadBMP_RW(SDL_RWops * src,
 /**
  *  Save a surface to a seekable SDL data stream (memory or file).
  *
+ *  Surfaces with a 24-bit, 32-bit and paletted 8-bit format get saved in the
+ *  BMP directly. Other RGB formats with 8-bit or higher get converted to a
+ *  24-bit surface or, if they have an alpha mask or a colorkey, to a 32-bit
+ *  surface before they are saved. YUV and paletted 1-bit and 4-bit formats are
+ *  not supported.
+ *
  *  If \c freedst is non-zero, the stream will be closed after being written.
  *
  *  \return 0 if successful or -1 if there was an error.

+ 21 - 1
Engine/lib/sdl/include/SDL_syswm.h

@@ -106,6 +106,10 @@ typedef struct ANativeWindow ANativeWindow;
 typedef void *EGLSurface;
 #endif
 
+#if defined(SDL_VIDEO_DRIVER_VIVANTE)
+#include "SDL_egl.h"
+#endif
+
 /**
  *  These are the various supported windowing subsystems
  */
@@ -120,7 +124,8 @@ typedef enum
     SDL_SYSWM_WAYLAND,
     SDL_SYSWM_MIR,
     SDL_SYSWM_WINRT,
-    SDL_SYSWM_ANDROID
+    SDL_SYSWM_ANDROID,
+    SDL_SYSWM_VIVANTE
 } SDL_SYSWM_TYPE;
 
 /**
@@ -166,6 +171,13 @@ struct SDL_SysWMmsg
             int dummy;
             /* No UIKit window events yet */
         } uikit;
+#endif
+#if defined(SDL_VIDEO_DRIVER_VIVANTE)
+        struct
+        {
+            int dummy;
+            /* No Vivante window events yet */
+        } vivante;
 #endif
         /* Can't have an empty union */
         int dummy;
@@ -259,6 +271,14 @@ struct SDL_SysWMinfo
         } android;
 #endif
 
+#if defined(SDL_VIDEO_DRIVER_VIVANTE)
+        struct
+        {
+            EGLNativeDisplayType display;
+            EGLNativeWindowType window;
+        } vivante;
+#endif
+
         /* Can't have an empty union */
         int dummy;
     } info;

+ 1 - 1
Engine/lib/sdl/include/SDL_version.h

@@ -59,7 +59,7 @@ typedef struct SDL_version
 */
 #define SDL_MAJOR_VERSION   2
 #define SDL_MINOR_VERSION   0
-#define SDL_PATCHLEVEL      4
+#define SDL_PATCHLEVEL      5
 
 /**
  *  \brief Macro to determine SDL version program was compiled against.

+ 124 - 9
Engine/lib/sdl/include/SDL_video.h

@@ -83,6 +83,7 @@ typedef struct
  *  \sa SDL_SetWindowPosition()
  *  \sa SDL_SetWindowSize()
  *  \sa SDL_SetWindowBordered()
+ *  \sa SDL_SetWindowResizable()
  *  \sa SDL_SetWindowTitle()
  *  \sa SDL_ShowWindow()
  */
@@ -95,6 +96,7 @@ typedef struct SDL_Window SDL_Window;
  */
 typedef enum
 {
+    /* !!! FIXME: change this to name = (1<<x). */
     SDL_WINDOW_FULLSCREEN = 0x00000001,         /**< fullscreen window */
     SDL_WINDOW_OPENGL = 0x00000002,             /**< window usable with OpenGL context */
     SDL_WINDOW_SHOWN = 0x00000004,              /**< window is visible */
@@ -109,13 +111,18 @@ typedef enum
     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_MOUSE_CAPTURE = 0x00004000       /**< window has mouse captured (unrelated to INPUT_GRABBED) */
+    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 */
+    SDL_WINDOW_UTILITY       = 0x00020000,      /**< window should be treated as a utility window */
+    SDL_WINDOW_TOOLTIP       = 0x00040000,      /**< window should be treated as a tooltip */
+    SDL_WINDOW_POPUP_MENU    = 0x00080000       /**< window should be treated as a popup menu */
 } SDL_WindowFlags;
 
 /**
  *  \brief Used to indicate that you don't care what the window position is.
  */
-#define SDL_WINDOWPOS_UNDEFINED_MASK    0x1FFF0000
+#define SDL_WINDOWPOS_UNDEFINED_MASK    0x1FFF0000u
 #define SDL_WINDOWPOS_UNDEFINED_DISPLAY(X)  (SDL_WINDOWPOS_UNDEFINED_MASK|(X))
 #define SDL_WINDOWPOS_UNDEFINED         SDL_WINDOWPOS_UNDEFINED_DISPLAY(0)
 #define SDL_WINDOWPOS_ISUNDEFINED(X)    \
@@ -124,7 +131,7 @@ typedef enum
 /**
  *  \brief Used to indicate that the window position should be centered.
  */
-#define SDL_WINDOWPOS_CENTERED_MASK    0x2FFF0000
+#define SDL_WINDOWPOS_CENTERED_MASK    0x2FFF0000u
 #define SDL_WINDOWPOS_CENTERED_DISPLAY(X)  (SDL_WINDOWPOS_CENTERED_MASK|(X))
 #define SDL_WINDOWPOS_CENTERED         SDL_WINDOWPOS_CENTERED_DISPLAY(0)
 #define SDL_WINDOWPOS_ISCENTERED(X)    \
@@ -154,8 +161,9 @@ typedef enum
     SDL_WINDOWEVENT_LEAVE,          /**< Window has lost mouse focus */
     SDL_WINDOWEVENT_FOCUS_GAINED,   /**< Window has gained keyboard focus */
     SDL_WINDOWEVENT_FOCUS_LOST,     /**< Window has lost keyboard focus */
-    SDL_WINDOWEVENT_CLOSE           /**< The window manager requests that the
-                                         window be closed */
+    SDL_WINDOWEVENT_CLOSE,          /**< The window manager requests that the window be closed */
+    SDL_WINDOWEVENT_TAKE_FOCUS,     /**< Window is being offered a focus (should SetWindowInputFocus() on itself or a subwindow, or ignore) */
+    SDL_WINDOWEVENT_HIT_TEST        /**< Window had a hit test that wasn't SDL_HITTEST_NORMAL. */
 } SDL_WindowEventID;
 
 /**
@@ -310,6 +318,25 @@ extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect * re
  */
 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
+ *
+ *  This is the same area as SDL_GetDisplayBounds() reports, but with portions
+ *  reserved by the system removed. For example, on Mac OS X, this subtracts
+ *  the area occupied by the menu bar and dock.
+ *
+ *  Setting a window to be fullscreen generally bypasses these unusable areas,
+ *  so these are good guidelines for the maximum space available to a
+ *  non-fullscreen window.
+ *
+ *  \return 0 on success, or -1 if the index is out of range.
+ *
+ *  \sa SDL_GetDisplayBounds()
+ *  \sa SDL_GetNumVideoDisplays()
+ */
+extern DECLSPEC int SDLCALL SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect);
+
 /**
  *  \brief Returns the number of available display modes.
  *
@@ -423,7 +450,7 @@ extern DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window * window);
  *               ::SDL_WINDOW_MINIMIZED,     ::SDL_WINDOW_INPUT_GRABBED,
  *               ::SDL_WINDOW_ALLOW_HIGHDPI.
  *
- *  \return The id of the window created, or zero if window creation failed.
+ *  \return The created window, or NULL if window creation failed.
  *
  *  If the window is created with the SDL_WINDOW_ALLOW_HIGHDPI flag, its size
  *  in pixels may differ from its size in screen coordinates on platforms with
@@ -442,7 +469,7 @@ extern DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
  *
  *  \param data A pointer to driver-dependent window creation data
  *
- *  \return The id of the window created, or zero if window creation failed.
+ *  \return The created window, or NULL if window creation failed.
  *
  *  \sa SDL_DestroyWindow()
  */
@@ -586,6 +613,25 @@ extern DECLSPEC void SDLCALL SDL_SetWindowSize(SDL_Window * window, int w,
 extern DECLSPEC void SDLCALL SDL_GetWindowSize(SDL_Window * window, int *w,
                                                int *h);
 
+/**
+ *  \brief Get the size of a window's borders (decorations) around the client area.
+ *
+ *  \param window The window to query.
+ *  \param top Pointer to variable for storing the size of the top border. NULL is permitted.
+ *  \param left Pointer to variable for storing the size of the left border. NULL is permitted.
+ *  \param bottom Pointer to variable for storing the size of the bottom border. NULL is permitted.
+ *  \param right Pointer to variable for storing the size of the right border. NULL is permitted.
+ *
+ *  \return 0 on success, or -1 if getting this information is not supported.
+ *
+ *  \note if this function fails (returns -1), the size values will be
+ *        initialized to 0, 0, 0, 0 (if a non-NULL pointer is provided), as
+ *        if the window in question was borderless.
+ */
+extern DECLSPEC int SDLCALL SDL_GetWindowBordersSize(SDL_Window * window,
+                                                     int *top, int *left,
+                                                     int *bottom, int *right);
+
 /**
  *  \brief Set the minimum size of a window's client area.
  *
@@ -661,6 +707,23 @@ extern DECLSPEC void SDLCALL SDL_GetWindowMaximumSize(SDL_Window * window,
 extern DECLSPEC void SDLCALL SDL_SetWindowBordered(SDL_Window * window,
                                                    SDL_bool bordered);
 
+/**
+ *  \brief Set the user-resizable state of a window.
+ *
+ *  This will add or remove the window's SDL_WINDOW_RESIZABLE flag and
+ *  allow/disallow user resizing of the window. This is a no-op if the
+ *  window's resizable state already matches the requested state.
+ *
+ *  \param window The window of which to change the resizable state.
+ *  \param resizable SDL_TRUE to allow resizing, SDL_FALSE to disallow.
+ *
+ *  \note You can't change the resizable state of a fullscreen window.
+ *
+ *  \sa SDL_GetWindowFlags()
+ */
+extern DECLSPEC void SDLCALL SDL_SetWindowResizable(SDL_Window * window,
+                                                    SDL_bool resizable);
+
 /**
  *  \brief Show a window.
  *
@@ -744,7 +807,7 @@ extern DECLSPEC int SDLCALL SDL_UpdateWindowSurface(SDL_Window * window);
  *  \return 0 on success, or -1 on error.
  *
  *  \sa SDL_GetWindowSurface()
- *  \sa SDL_UpdateWindowSurfaceRect()
+ *  \sa SDL_UpdateWindowSurface()
  */
 extern DECLSPEC int SDLCALL SDL_UpdateWindowSurfaceRects(SDL_Window * window,
                                                          const SDL_Rect * rects,
@@ -801,6 +864,58 @@ extern DECLSPEC int SDLCALL SDL_SetWindowBrightness(SDL_Window * window, float b
  */
 extern DECLSPEC float SDLCALL SDL_GetWindowBrightness(SDL_Window * window);
 
+/**
+ *  \brief Set the opacity for a window
+ *
+ *  \param window The window which will be made transparent or opaque
+ *  \param opacity Opacity (0.0f - transparent, 1.0f - opaque) This will be
+ *                 clamped internally between 0.0f and 1.0f.
+ * 
+ *  \return 0 on success, or -1 if setting the opacity isn't supported.
+ *
+ *  \sa SDL_GetWindowOpacity()
+ */
+extern DECLSPEC int SDLCALL SDL_SetWindowOpacity(SDL_Window * window, float opacity);
+
+/**
+ *  \brief Get the opacity of a window.
+ *
+ *  If transparency isn't supported on this platform, opacity will be reported
+ *  as 1.0f without error.
+ *
+ *  \param window The window in question.
+ *  \param out_opacity Opacity (0.0f - transparent, 1.0f - opaque)
+ *
+ *  \return 0 on success, or -1 on error (invalid window, etc).
+ *
+ *  \sa SDL_SetWindowOpacity()
+ */
+extern DECLSPEC int SDLCALL SDL_GetWindowOpacity(SDL_Window * window, float * out_opacity);
+
+/**
+ *  \brief Sets the window as a modal for another window (TODO: reconsider this function and/or its name)
+ *
+ *  \param modal_window The window that should be modal
+ *  \param parent_window The parent window
+ * 
+ *  \return 0 on success, or -1 otherwise.
+ */
+extern DECLSPEC int SDLCALL SDL_SetWindowModalFor(SDL_Window * modal_window, SDL_Window * parent_window);
+
+/**
+ *  \brief Explicitly sets input focus to the window.
+ *
+ *  You almost certainly want SDL_RaiseWindow() instead of this function. Use
+ *  this with caution, as you might give focus to a window that's completely
+ *  obscured by other windows.
+ *
+ *  \param window The window that should get the input focus
+ * 
+ *  \return 0 on success, or -1 otherwise.
+ *  \sa SDL_RaiseWindow()
+ */
+extern DECLSPEC int SDLCALL SDL_SetWindowInputFocus(SDL_Window * window);
+
 /**
  *  \brief Set the gamma ramp for a window.
  *
@@ -920,7 +1035,7 @@ extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window);
 
 
 /**
- *  \brief Returns whether the screensaver is currently enabled (default on).
+ *  \brief Returns whether the screensaver is currently enabled (default off).
  *
  *  \sa SDL_EnableScreenSaver()
  *  \sa SDL_DisableScreenSaver()

+ 12 - 10
Engine/lib/sdl/src/SDL.c

@@ -115,6 +115,16 @@ SDL_InitSubSystem(Uint32 flags)
     /* Clear the error message */
     SDL_ClearError();
 
+    if ((flags & SDL_INIT_GAMECONTROLLER)) {
+        /* game controller implies joystick */
+        flags |= SDL_INIT_JOYSTICK;
+    }
+
+    if ((flags & (SDL_INIT_VIDEO|SDL_INIT_JOYSTICK))) {
+        /* video or joystick implies events */
+        flags |= SDL_INIT_EVENTS;
+    }
+
 #if SDL_VIDEO_DRIVER_WINDOWS
 	if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
 		if (SDL_HelperWindowCreate() < 0) {
@@ -127,16 +137,6 @@ SDL_InitSubSystem(Uint32 flags)
     SDL_TicksInit();
 #endif
 
-    if ((flags & SDL_INIT_GAMECONTROLLER)) {
-        /* game controller implies joystick */
-        flags |= SDL_INIT_JOYSTICK;
-    }
-
-    if ((flags & (SDL_INIT_VIDEO|SDL_INIT_JOYSTICK))) {
-        /* video or joystick implies events */
-        flags |= SDL_INIT_EVENTS;
-    }
-
     /* Initialize the event subsystem */
     if ((flags & SDL_INIT_EVENTS)) {
 #if !SDL_EVENTS_DISABLED
@@ -443,6 +443,8 @@ SDL_GetPlatform()
     return "Windows";
 #elif __WINRT__
     return "WinRT";
+#elif __TVOS__
+    return "tvOS";
 #elif __IPHONEOS__
     return "iOS";
 #elif __PSP__

+ 7 - 0
Engine/lib/sdl/src/SDL_error.c

@@ -116,6 +116,10 @@ SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
     return -1;
 }
 
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
 /* This function has a bit more overhead than most error functions
    so that it supports internationalization and thread-safe errors.
 */
@@ -216,6 +220,9 @@ SDL_GetErrorMsg(char *errstr, int maxlen)
     }
     return (errstr);
 }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 /* Available for backwards compatibility */
 const char *

+ 13 - 0
Engine/lib/sdl/src/SDL_hints.c

@@ -118,6 +118,19 @@ SDL_GetHint(const char *name)
     return env;
 }
 
+SDL_bool
+SDL_GetHintBoolean(const char *name, SDL_bool default_value)
+{
+    const char *hint = SDL_GetHint(name);
+    if (!hint) {
+        return default_value;
+    }
+    if (*hint == '0' || SDL_strcasecmp(hint, "false") == 0) {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
 void
 SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
 {

+ 4 - 4
Engine/lib/sdl/src/SDL_log.c

@@ -324,7 +324,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
         size_t length;
         LPTSTR tstr;
 
-#ifndef __WINRT__
+#if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
         BOOL attachResult;
         DWORD attachError;
         unsigned long charsWritten; 
@@ -356,7 +356,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
                 }
         }
-#endif /* ifndef __WINRT__ */
+#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);
@@ -366,7 +366,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
         /* Output to debugger */
         OutputDebugString(tstr);
        
-#ifndef __WINRT__
+#if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
         /* Screen output to stderr, if console was attached. */
         if (consoleAttached == 1) {
                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
@@ -376,7 +376,7 @@ SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
                     }
                 }
         }
-#endif /* ifndef __WINRT__ */
+#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
 
         SDL_free(tstr);
         SDL_stack_free(output);

+ 382 - 169
Engine/lib/sdl/src/audio/SDL_audio.c

@@ -25,33 +25,29 @@
 #include "SDL.h"
 #include "SDL_audio.h"
 #include "SDL_audio_c.h"
-#include "SDL_audiomem.h"
 #include "SDL_sysaudio.h"
+#include "../thread/SDL_systhread.h"
 
 #define _THIS SDL_AudioDevice *_this
 
 static SDL_AudioDriver current_audio;
 static SDL_AudioDevice *open_devices[16];
 
-/* !!! FIXME: These are wordy and unlocalized... */
-#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
-#define DEFAULT_INPUT_DEVNAME "System audio capture device"
-
-
 /*
  * Not all of these will be compiled and linked in, but it's convenient
  *  to have a complete list here and saves yet-another block of #ifdefs...
  *  Please see bootstrap[], below, for the actual #ifdef mess.
  */
+extern AudioBootStrap PULSEAUDIO_bootstrap;
+extern AudioBootStrap ALSA_bootstrap;
+extern AudioBootStrap SNDIO_bootstrap;
 extern AudioBootStrap BSD_AUDIO_bootstrap;
 extern AudioBootStrap DSP_bootstrap;
-extern AudioBootStrap ALSA_bootstrap;
-extern AudioBootStrap PULSEAUDIO_bootstrap;
 extern AudioBootStrap QSAAUDIO_bootstrap;
 extern AudioBootStrap SUNAUDIO_bootstrap;
 extern AudioBootStrap ARTS_bootstrap;
 extern AudioBootStrap ESD_bootstrap;
-extern AudioBootStrap NACLAUD_bootstrap;
+extern AudioBootStrap NACLAUDIO_bootstrap;
 extern AudioBootStrap NAS_bootstrap;
 extern AudioBootStrap XAUDIO2_bootstrap;
 extern AudioBootStrap DSOUND_bootstrap;
@@ -59,18 +55,13 @@ extern AudioBootStrap WINMM_bootstrap;
 extern AudioBootStrap PAUDIO_bootstrap;
 extern AudioBootStrap HAIKUAUDIO_bootstrap;
 extern AudioBootStrap COREAUDIO_bootstrap;
-extern AudioBootStrap SNDMGR_bootstrap;
-extern AudioBootStrap DISKAUD_bootstrap;
-extern AudioBootStrap DUMMYAUD_bootstrap;
-extern AudioBootStrap DCAUD_bootstrap;
-extern AudioBootStrap DART_bootstrap;
-extern AudioBootStrap NDSAUD_bootstrap;
+extern AudioBootStrap DISKAUDIO_bootstrap;
+extern AudioBootStrap DUMMYAUDIO_bootstrap;
 extern AudioBootStrap FUSIONSOUND_bootstrap;
-extern AudioBootStrap ANDROIDAUD_bootstrap;
-extern AudioBootStrap PSPAUD_bootstrap;
+extern AudioBootStrap ANDROIDAUDIO_bootstrap;
+extern AudioBootStrap PSPAUDIO_bootstrap;
 extern AudioBootStrap SNDIO_bootstrap;
-extern AudioBootStrap EmscriptenAudio_bootstrap;
-
+extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
 
 /* Available audio drivers */
 static const AudioBootStrap *const bootstrap[] = {
@@ -102,7 +93,7 @@ static const AudioBootStrap *const bootstrap[] = {
     &ESD_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_NACL
-   &NACLAUD_bootstrap,
+    &NACLAUDIO_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_NAS
     &NAS_bootstrap,
@@ -126,22 +117,22 @@ static const AudioBootStrap *const bootstrap[] = {
     &COREAUDIO_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_DISK
-    &DISKAUD_bootstrap,
+    &DISKAUDIO_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_DUMMY
-    &DUMMYAUD_bootstrap,
+    &DUMMYAUDIO_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_FUSIONSOUND
     &FUSIONSOUND_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_ANDROID
-    &ANDROIDAUD_bootstrap,
+    &ANDROIDAUDIO_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_PSP
-    &PSPAUD_bootstrap,
+    &PSPAUDIO_bootstrap,
 #endif
 #if SDL_AUDIO_DRIVER_EMSCRIPTEN
-    &EmscriptenAudio_bootstrap,
+    &EMSCRIPTENAUDIO_bootstrap,
 #endif
     NULL
 };
@@ -165,7 +156,7 @@ SDL_AudioDetectDevices_Default(void)
 {
     /* you have to write your own implementation if these assertions fail. */
     SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice);
-    SDL_assert(current_audio.impl.OnlyHasDefaultInputDevice || !current_audio.impl.HasCaptureSupport);
+    SDL_assert(current_audio.impl.OnlyHasDefaultCaptureDevice || !current_audio.impl.HasCaptureSupport);
 
     SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) ((size_t) 0x1));
     if (current_audio.impl.HasCaptureSupport) {
@@ -200,8 +191,19 @@ SDL_AudioGetDeviceBuf_Default(_THIS)
     return NULL;
 }
 
+static int
+SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen)
+{
+    return -1;  /* just fail immediately. */
+}
+
+static void
+SDL_AudioFlushCapture_Default(_THIS)
+{                               /* no-op. */
+}
+
 static void
-SDL_AudioWaitDone_Default(_THIS)
+SDL_AudioPrepareToClose_Default(_THIS)
 {                               /* no-op. */
 }
 
@@ -257,15 +259,28 @@ SDL_AudioUnlockDevice_Default(SDL_AudioDevice * device)
     }
 }
 
+static void
+SDL_AudioLockOrUnlockDeviceWithNoMixerLock(SDL_AudioDevice * device)
+{
+}
 
 static void
-finalize_audio_entry_points(void)
+finish_audio_entry_points_init(void)
 {
     /*
      * Fill in stub functions for unused driver entry points. This lets us
      *  blindly call them without having to check for validity first.
      */
 
+    if (current_audio.impl.SkipMixerLock) {
+        if (current_audio.impl.LockDevice == NULL) {
+            current_audio.impl.LockDevice = SDL_AudioLockOrUnlockDeviceWithNoMixerLock;
+        }
+        if (current_audio.impl.UnlockDevice == NULL) {
+            current_audio.impl.UnlockDevice = SDL_AudioLockOrUnlockDeviceWithNoMixerLock;
+        }
+    }
+
 #define FILL_STUB(x) \
         if (current_audio.impl.x == NULL) { \
             current_audio.impl.x = SDL_Audio##x##_Default; \
@@ -277,7 +292,9 @@ finalize_audio_entry_points(void)
     FILL_STUB(PlayDevice);
     FILL_STUB(GetPendingBytes);
     FILL_STUB(GetDeviceBuf);
-    FILL_STUB(WaitDone);
+    FILL_STUB(CaptureFromDevice);
+    FILL_STUB(FlushCapture);
+    FILL_STUB(PrepareToClose);
     FILL_STUB(CloseDevice);
     FILL_STUB(LockDevice);
     FILL_STUB(UnlockDevice);
@@ -316,7 +333,7 @@ add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices,
 static SDL_INLINE int
 add_capture_device(const char *name, void *handle)
 {
-    /* !!! FIXME: add this later. SDL_assert(current_audio.impl.HasCaptureSupport);*/
+    SDL_assert(current_audio.impl.HasCaptureSupport);
     return add_audio_device(name, handle, &current_audio.inputDevices, &current_audio.inputDeviceCount);
 }
 
@@ -365,14 +382,14 @@ void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
 {
     SDL_assert(get_audio_device(device->id) == device);
 
-    if (!device->enabled) {
+    if (!SDL_AtomicGet(&device->enabled)) {
         return;
     }
 
     /* Ends the audio callback and mark the device as STOPPED, but the
        app still needs to close the device to free resources. */
     current_audio.impl.LockDevice(device);
-    device->enabled = 0;
+    SDL_AtomicSet(&device->enabled, 0);
     current_audio.impl.UnlockDevice(device);
 
     /* Post the event, if desired */
@@ -404,13 +421,26 @@ mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *remove
 void
 SDL_RemoveAudioDevice(const int iscapture, void *handle)
 {
+    int device_index;
+    SDL_AudioDevice *device = NULL;
+
     SDL_LockMutex(current_audio.detectionLock);
     if (iscapture) {
         mark_device_removed(handle, current_audio.inputDevices, &current_audio.captureDevicesRemoved);
     } else {
         mark_device_removed(handle, current_audio.outputDevices, &current_audio.outputDevicesRemoved);
     }
+    for (device_index = 0; device_index < SDL_arraysize(open_devices); device_index++)
+    {
+        device = open_devices[device_index];
+        if (device != NULL && device->handle == handle)
+        {
+            SDL_OpenedAudioDeviceDisconnected(device);
+            break;
+        }
+    }
     SDL_UnlockMutex(current_audio.detectionLock);
+
     current_audio.impl.FreeDeviceHandle(handle);
 }
 
@@ -420,77 +450,24 @@ SDL_RemoveAudioDevice(const int iscapture, void *handle)
 
 /* this expects that you managed thread safety elsewhere. */
 static void
-free_audio_queue(SDL_AudioBufferQueue *buffer)
-{
-    while (buffer) {
-        SDL_AudioBufferQueue *next = buffer->next;
-        SDL_free(buffer);
-        buffer = next;
-    }
-}
-
-static void SDLCALL
-SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int _len)
+free_audio_queue(SDL_AudioBufferQueue *packet)
 {
-    /* this function always holds the mixer lock before being called. */
-    Uint32 len = (Uint32) _len;
-    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
-    SDL_AudioBufferQueue *buffer;
-
-    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
-    SDL_assert(_len >= 0);  /* this shouldn't ever happen, right?! */
-
-    while ((len > 0) && ((buffer = device->buffer_queue_head) != NULL)) {
-        const Uint32 avail = buffer->datalen - buffer->startpos;
-        const Uint32 cpy = SDL_min(len, avail);
-        SDL_assert(device->queued_bytes >= avail);
-
-        SDL_memcpy(stream, buffer->data + buffer->startpos, cpy);
-        buffer->startpos += cpy;
-        stream += cpy;
-        device->queued_bytes -= cpy;
-        len -= cpy;
-
-        if (buffer->startpos == buffer->datalen) {  /* packet is done, put it in the pool. */
-            device->buffer_queue_head = buffer->next;
-            SDL_assert((buffer->next != NULL) || (buffer == device->buffer_queue_tail));
-            buffer->next = device->buffer_queue_pool;
-            device->buffer_queue_pool = buffer;
-        }
-    }
-
-    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
-
-    if (len > 0) {  /* fill any remaining space in the stream with silence. */
-        SDL_assert(device->buffer_queue_head == NULL);
-        SDL_memset(stream, device->spec.silence, len);
-    }
-
-    if (device->buffer_queue_head == NULL) {
-        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
+    while (packet) {
+        SDL_AudioBufferQueue *next = packet->next;
+        SDL_free(packet);
+        packet = next;
     }
 }
 
-int
-SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
+/* NOTE: This assumes you'll hold the mixer lock before calling! */
+static int
+queue_audio_to_device(SDL_AudioDevice *device, const Uint8 *data, Uint32 len)
 {
-    SDL_AudioDevice *device = get_audio_device(devid);
-    const Uint8 *data = (const Uint8 *) _data;
     SDL_AudioBufferQueue *orighead;
     SDL_AudioBufferQueue *origtail;
     Uint32 origlen;
     Uint32 datalen;
 
-    if (!device) {
-        return -1;  /* get_audio_device() will have set the error state */
-    }
-
-    if (device->spec.callback != SDL_BufferQueueDrainCallback) {
-        return SDL_SetError("Audio device has a callback, queueing not allowed");
-    }
-
-    current_audio.impl.LockDevice(device);
-
     orighead = device->buffer_queue_head;
     origtail = device->buffer_queue_tail;
     origlen = origtail ? origtail->datalen : 0;
@@ -520,8 +497,6 @@ SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
                     device->buffer_queue_tail = origtail;
                     device->buffer_queue_pool = NULL;
 
-                    current_audio.impl.UnlockDevice(device);
-
                     free_audio_queue(packet);  /* give back what we can. */
 
                     return SDL_OutOfMemory();
@@ -548,22 +523,142 @@ SDL_QueueAudio(SDL_AudioDeviceID devid, const void *_data, Uint32 len)
         device->queued_bytes += datalen;
     }
 
-    current_audio.impl.UnlockDevice(device);
-
     return 0;
 }
 
+/* NOTE: This assumes you'll hold the mixer lock before calling! */
+static Uint32
+dequeue_audio_from_device(SDL_AudioDevice *device, Uint8 *stream, Uint32 len)
+{
+    SDL_AudioBufferQueue *packet;
+    Uint8 *ptr = stream;
+
+    while ((len > 0) && ((packet = device->buffer_queue_head) != NULL)) {
+        const Uint32 avail = packet->datalen - packet->startpos;
+        const Uint32 cpy = SDL_min(len, avail);
+        SDL_assert(device->queued_bytes >= avail);
+
+        SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
+        packet->startpos += cpy;
+        ptr += cpy;
+        device->queued_bytes -= cpy;
+        len -= cpy;
+
+        if (packet->startpos == packet->datalen) {  /* packet is done, put it in the pool. */
+            device->buffer_queue_head = packet->next;
+            SDL_assert((packet->next != NULL) || (packet == device->buffer_queue_tail));
+            packet->next = device->buffer_queue_pool;
+            device->buffer_queue_pool = packet;
+        }
+    }
+
+    SDL_assert((device->buffer_queue_head != NULL) == (device->queued_bytes != 0));
+
+    if (device->buffer_queue_head == NULL) {
+        device->buffer_queue_tail = NULL;  /* in case we drained the queue entirely. */
+    }
+
+    return (Uint32) (ptr - stream);
+}
+
+static void SDLCALL
+SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int len)
+{
+    /* this function always holds the mixer lock before being called. */
+    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
+    Uint32 written;
+
+    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
+    SDL_assert(!device->iscapture);  /* this shouldn't ever happen, right?! */
+    SDL_assert(len >= 0);  /* this shouldn't ever happen, right?! */
+
+    written = dequeue_audio_from_device(device, stream, (Uint32) len);
+    stream += written;
+    len -= (int) written;
+
+    if (len > 0) {  /* fill any remaining space in the stream with silence. */
+        SDL_assert(device->buffer_queue_head == NULL);
+        SDL_memset(stream, device->spec.silence, len);
+    }
+}
+
+static void SDLCALL
+SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len)
+{
+    /* this function always holds the mixer lock before being called. */
+    SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
+
+    SDL_assert(device != NULL);  /* this shouldn't ever happen, right?! */
+    SDL_assert(device->iscapture);  /* this shouldn't ever happen, right?! */
+    SDL_assert(len >= 0);  /* this shouldn't ever happen, right?! */
+
+    /* note that if this needs to allocate more space and run out of memory,
+       we have no choice but to quietly drop the data and hope it works out
+       later, but you probably have bigger problems in this case anyhow. */
+    queue_audio_to_device(device, stream, (Uint32) len);
+}
+
+int
+SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len)
+{
+    SDL_AudioDevice *device = get_audio_device(devid);
+    int rc = 0;
+
+    if (!device) {
+        return -1;  /* get_audio_device() will have set the error state */
+    } else if (device->iscapture) {
+        return SDL_SetError("This is a capture device, queueing not allowed");
+    } else if (device->spec.callback != SDL_BufferQueueDrainCallback) {
+        return SDL_SetError("Audio device has a callback, queueing not allowed");
+    }
+
+    if (len > 0) {
+        current_audio.impl.LockDevice(device);
+        rc = queue_audio_to_device(device, data, len);
+        current_audio.impl.UnlockDevice(device);
+    }
+
+    return rc;
+}
+
+Uint32
+SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len)
+{
+    SDL_AudioDevice *device = get_audio_device(devid);
+    Uint32 rc;
+
+    if ( (len == 0) ||  /* nothing to do? */
+         (!device) ||  /* called with bogus device id */
+         (!device->iscapture) ||  /* playback devices can't dequeue */
+         (device->spec.callback != SDL_BufferQueueFillCallback) ) { /* not set for queueing */
+        return 0;  /* just report zero bytes dequeued. */
+    }
+
+    current_audio.impl.LockDevice(device);
+    rc = dequeue_audio_from_device(device, data, len);
+    current_audio.impl.UnlockDevice(device);
+    return rc;
+}
+
 Uint32
 SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid)
 {
     Uint32 retval = 0;
     SDL_AudioDevice *device = get_audio_device(devid);
 
+    if (!device) {
+        return 0;
+    }
+
     /* Nothing to do unless we're set up for queueing. */
-    if (device && (device->spec.callback == SDL_BufferQueueDrainCallback)) {
+    if (device->spec.callback == SDL_BufferQueueDrainCallback) {
         current_audio.impl.LockDevice(device);
         retval = device->queued_bytes + current_audio.impl.GetPendingBytes(device);
         current_audio.impl.UnlockDevice(device);
+    } else if (device->spec.callback == SDL_BufferQueueFillCallback) {
+        current_audio.impl.LockDevice(device);
+        retval = device->queued_bytes;
+        current_audio.impl.UnlockDevice(device);
     }
 
     return retval;
@@ -573,25 +668,49 @@ void
 SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
 {
     SDL_AudioDevice *device = get_audio_device(devid);
-    SDL_AudioBufferQueue *buffer = NULL;
+    SDL_AudioBufferQueue *packet;
+
     if (!device) {
         return;  /* nothing to do. */
     }
 
     /* Blank out the device and release the mutex. Free it afterwards. */
     current_audio.impl.LockDevice(device);
-    buffer = device->buffer_queue_head;
+
+    /* merge the available pool and the current queue into one list. */
+    packet = device->buffer_queue_head;
+    if (packet) {
+        device->buffer_queue_tail->next = device->buffer_queue_pool;
+    } else {
+        packet = device->buffer_queue_pool;
+    }
+
+    /* Remove the queued packets from the device. */
     device->buffer_queue_tail = NULL;
     device->buffer_queue_head = NULL;
     device->queued_bytes = 0;
+    device->buffer_queue_pool = packet;
+
+    /* Keep up to two packets in the pool to reduce future malloc pressure. */
+    if (packet) {
+        if (!packet->next) {
+            packet = NULL;  /* one packet (the only one) for the pool. */
+        } else {
+            SDL_AudioBufferQueue *next = packet->next->next;
+            packet->next->next = NULL;  /* two packets for the pool. */
+            packet = next;  /* rest will be freed. */
+        }
+    }
+
     current_audio.impl.UnlockDevice(device);
 
-    free_audio_queue(buffer);
+    /* free any extra packets we didn't keep in the pool. */
+    free_audio_queue(packet);
 }
 
 
 /* The general mixing thread function */
-int SDLCALL
+static int SDLCALL
 SDL_RunAudio(void *devicep)
 {
     SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
@@ -600,7 +719,9 @@ SDL_RunAudio(void *devicep)
     const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
     Uint8 *stream;
     void *udata = device->spec.userdata;
-    void (SDLCALL *fill) (void *, Uint8 *, int) = device->spec.callback;
+    void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
+
+    SDL_assert(!device->iscapture);
 
     /* The audio mixing is always a high priority thread */
     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
@@ -610,11 +731,11 @@ SDL_RunAudio(void *devicep)
     current_audio.impl.ThreadInit(device);
 
     /* Loop, filling the audio buffers */
-    while (!device->shutdown) {
+    while (!SDL_AtomicGet(&device->shutdown)) {
         /* Fill the current buffer with sound */
         if (device->convert.needed) {
             stream = device->convert.buf;
-        } else if (device->enabled) {
+        } else if (SDL_AtomicGet(&device->enabled)) {
             stream = current_audio.impl.GetDeviceBuf(device);
         } else {
             /* if the device isn't enabled, we still write to the
@@ -630,16 +751,18 @@ SDL_RunAudio(void *devicep)
         }
 
         /* !!! FIXME: this should be LockDevice. */
-        SDL_LockMutex(device->mixer_lock);
-        if (device->paused) {
-            SDL_memset(stream, silence, stream_len);
-        } else {
-            (*fill) (udata, stream, stream_len);
+        if ( SDL_AtomicGet(&device->enabled) ) {
+            SDL_LockMutex(device->mixer_lock);
+            if (SDL_AtomicGet(&device->paused)) {
+                SDL_memset(stream, silence, stream_len);
+            } else {
+                (*callback) (udata, stream, stream_len);
+            }
+            SDL_UnlockMutex(device->mixer_lock);
         }
-        SDL_UnlockMutex(device->mixer_lock);
 
         /* Convert the audio if necessary */
-        if (device->enabled && device->convert.needed) {
+        if (device->convert.needed && SDL_AtomicGet(&device->enabled)) {
             SDL_ConvertAudio(&device->convert);
             stream = current_audio.impl.GetDeviceBuf(device);
             if (stream == NULL) {
@@ -659,8 +782,91 @@ SDL_RunAudio(void *devicep)
         }
     }
 
+    current_audio.impl.PrepareToClose(device);
+
     /* Wait for the audio to drain. */
-    current_audio.impl.WaitDone(device);
+    SDL_Delay(((device->spec.samples * 1000) / device->spec.freq) * 2);
+
+    return 0;
+}
+
+/* The general capture thread function */
+static int SDLCALL
+SDL_CaptureAudio(void *devicep)
+{
+    SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
+    const int silence = (int) device->spec.silence;
+    const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
+    const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
+    Uint8 *stream;
+    void *udata = device->spec.userdata;
+    void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
+
+    SDL_assert(device->iscapture);
+
+    /* The audio mixing is always a high priority thread */
+    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+
+    /* Perform any thread setup */
+    device->threadid = SDL_ThreadID();
+    current_audio.impl.ThreadInit(device);
+
+    /* Loop, filling the audio buffers */
+    while (!SDL_AtomicGet(&device->shutdown)) {
+        int still_need;
+        Uint8 *ptr;
+
+        if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) {
+            SDL_Delay(delay);  /* just so we don't cook the CPU. */
+            current_audio.impl.FlushCapture(device);  /* dump anything pending. */
+            continue;
+        }
+
+        /* Fill the current buffer with sound */
+        still_need = stream_len;
+        if (device->convert.needed) {
+            ptr = stream = device->convert.buf;
+        } else {
+            /* just use the "fake" stream to hold data read from the device. */
+            ptr = stream = device->fake_stream;
+        }
+
+        /* We still read from the device when "paused" to keep the state sane,
+           and block when there isn't data so this thread isn't eating CPU.
+           But we don't process it further or call the app's callback. */
+
+        while (still_need > 0) {
+            const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need);
+            SDL_assert(rc <= still_need);  /* device should not overflow buffer. :) */
+            if (rc > 0) {
+                still_need -= rc;
+                ptr += rc;
+            } else {  /* uhoh, device failed for some reason! */
+                SDL_OpenedAudioDeviceDisconnected(device);
+                break;
+            }
+        }
+
+        if (still_need > 0) {
+            /* Keep any data we already read, silence the rest. */
+            SDL_memset(ptr, silence, still_need);
+        }
+
+        if (device->convert.needed) {
+            SDL_ConvertAudio(&device->convert);
+        }
+
+        /* !!! FIXME: this should be LockDevice. */
+        SDL_LockMutex(device->mixer_lock);
+        if (SDL_AtomicGet(&device->paused)) {
+            current_audio.impl.FlushCapture(device);  /* one snuck in! */
+        } else {
+            (*callback)(udata, stream, stream_len);
+        }
+        SDL_UnlockMutex(device->mixer_lock);
+    }
+
+    current_audio.impl.FlushCapture(device);
 
     return 0;
 }
@@ -757,7 +963,7 @@ SDL_AudioInit(const char *driver_name)
 
     current_audio.detectionLock = SDL_CreateMutex();
 
-    finalize_audio_entry_points();
+    finish_audio_entry_points_init();
 
     /* Make sure we have a list of devices available at startup. */
     current_audio.impl.DetectDevices();
@@ -872,27 +1078,38 @@ SDL_GetAudioDeviceName(int index, int iscapture)
 static void
 close_audio_device(SDL_AudioDevice * device)
 {
-    device->enabled = 0;
-    device->shutdown = 1;
+    if (!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;
+        }
+    }
+
+    SDL_AtomicSet(&device->shutdown, 1);
+    SDL_AtomicSet(&device->enabled, 0);
     if (device->thread != NULL) {
         SDL_WaitThread(device->thread, NULL);
     }
     if (device->mixer_lock != NULL) {
         SDL_DestroyMutex(device->mixer_lock);
     }
-    SDL_FreeAudioMem(device->fake_stream);
+    SDL_free(device->fake_stream);
     if (device->convert.needed) {
-        SDL_FreeAudioMem(device->convert.buf);
+        SDL_free(device->convert.buf);
     }
-    if (device->opened) {
+    if (device->hidden != NULL) {
         current_audio.impl.CloseDevice(device);
-        device->opened = 0;
     }
 
     free_audio_queue(device->buffer_queue_head);
     free_audio_queue(device->buffer_queue_pool);
 
-    SDL_FreeAudioMem(device);
+    SDL_free(device);
 }
 
 
@@ -963,12 +1180,12 @@ open_audio_device(const char *devname, int iscapture,
                   const SDL_AudioSpec * desired, SDL_AudioSpec * obtained,
                   int allowed_changes, int min_id)
 {
+    const SDL_bool is_internal_thread = (desired->callback != NULL);
     SDL_AudioDeviceID id = 0;
     SDL_AudioSpec _obtained;
     SDL_AudioDevice *device;
     SDL_bool build_cvt;
     void *handle = NULL;
-    Uint32 stream_len;
     int i = 0;
 
     if (!SDL_WasInit(SDL_INIT_AUDIO)) {
@@ -981,6 +1198,7 @@ open_audio_device(const char *devname, int iscapture,
         return 0;
     }
 
+    /* !!! FIXME: there is a race condition here if two devices open from two threads at once. */
     /* Find an available device ID... */
     for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
         if (open_devices[id] == NULL) {
@@ -1015,7 +1233,7 @@ open_audio_device(const char *devname, int iscapture,
      *  opens of the default system device.
      */
 
-    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
+    if ((iscapture) && (current_audio.impl.OnlyHasDefaultCaptureDevice)) {
         if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
             SDL_SetError("No such device");
             return 0;
@@ -1067,17 +1285,19 @@ open_audio_device(const char *devname, int iscapture,
         }
     }
 
-    device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
+    device = (SDL_AudioDevice *) SDL_calloc(1, sizeof (SDL_AudioDevice));
     if (device == NULL) {
         SDL_OutOfMemory();
         return 0;
     }
-    SDL_zerop(device);
     device->id = id + 1;
     device->spec = *obtained;
-    device->enabled = 1;
-    device->paused = 1;
-    device->iscapture = iscapture;
+    device->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
+    device->handle = handle;
+
+    SDL_AtomicSet(&device->shutdown, 0);  /* just in case. */
+    SDL_AtomicSet(&device->paused, 1);
+    SDL_AtomicSet(&device->enabled, 1);
 
     /* Create a mutex for locking the sound buffers */
     if (!current_audio.impl.SkipMixerLock) {
@@ -1093,7 +1313,10 @@ open_audio_device(const char *devname, int iscapture,
         close_audio_device(device);
         return 0;
     }
-    device->opened = 1;
+
+    /* if your target really doesn't need it, set it to 0x1 or something. */
+    /* otherwise, close_audio_device() won't call impl.CloseDevice(). */
+    SDL_assert(device->hidden != NULL);
 
     /* See if we need to do any conversion */
     build_cvt = SDL_FALSE;
@@ -1143,7 +1366,7 @@ open_audio_device(const char *devname, int iscapture,
                                          device->convert.len_ratio);
 
             device->convert.buf =
-                (Uint8 *) SDL_AllocAudioMem(device->convert.len *
+                (Uint8 *) SDL_malloc(device->convert.len *
                                             device->convert.len_mult);
             if (device->convert.buf == NULL) {
                 close_audio_device(device);
@@ -1153,19 +1376,6 @@ open_audio_device(const char *devname, int iscapture,
         }
     }
 
-    /* Allocate a fake audio memory buffer */
-    stream_len = (device->convert.needed) ? device->convert.len_cvt : 0;
-    if (device->spec.size > stream_len) {
-        stream_len = device->spec.size;
-    }
-    SDL_assert(stream_len > 0);
-    device->fake_stream = (Uint8 *)SDL_AllocAudioMem(stream_len);
-    if (device->fake_stream == NULL) {
-        close_audio_device(device);
-        SDL_OutOfMemory();
-        return 0;
-    }
-
     if (device->spec.callback == NULL) {  /* use buffer queueing? */
         /* pool a few packets to start. Enough for two callbacks. */
         const int packetlen = SDL_AUDIOBUFFERQUEUE_PACKETLEN;
@@ -1181,7 +1391,7 @@ open_audio_device(const char *devname, int iscapture,
             }
         }
 
-        device->spec.callback = SDL_BufferQueueDrainCallback;
+        device->spec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback;
         device->spec.userdata = device;
     }
 
@@ -1191,21 +1401,30 @@ open_audio_device(const char *devname, int iscapture,
     /* Start the audio thread if necessary */
     if (!current_audio.impl.ProvidesOwnCallbackThread) {
         /* Start the audio thread */
-        char name[64];
-        SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
-/* !!! FIXME: this is nasty. */
-#if defined(__WIN32__) && !defined(HAVE_LIBC)
-#undef SDL_CreateThread
-#if SDL_DYNAMIC_API
-        device->thread = SDL_CreateThread_REAL(SDL_RunAudio, name, device, NULL, NULL);
-#else
-        device->thread = SDL_CreateThread(SDL_RunAudio, name, device, NULL, NULL);
-#endif
-#else
-        device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
-#endif
+        /* !!! FIXME: we don't force the audio thread stack size here if it calls into user code, but maybe we should? */
+        /* buffer queueing callback only needs a few bytes, so make the stack tiny. */
+        const size_t stacksize = is_internal_thread ? 64 * 1024 : 0;
+        char threadname[64];
+
+        /* Allocate a fake audio buffer; only used by our internal threads. */
+        Uint32 stream_len = (device->convert.needed) ? device->convert.len_cvt : 0;
+        if (device->spec.size > stream_len) {
+            stream_len = device->spec.size;
+        }
+        SDL_assert(stream_len > 0);
+
+        device->fake_stream = (Uint8 *) SDL_malloc(stream_len);
+        if (device->fake_stream == NULL) {
+            close_audio_device(device);
+            SDL_OutOfMemory();
+            return 0;
+        }
+
+        SDL_snprintf(threadname, sizeof (threadname), "SDLAudioDev%d", (int) device->id);
+        device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, threadname, stacksize, device);
+
         if (device->thread == NULL) {
-            SDL_CloseAudioDevice(device->id);
+            close_audio_device(device);
             SDL_SetError("Couldn't create audio thread");
             return 0;
         }
@@ -1258,8 +1477,8 @@ SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid)
 {
     SDL_AudioDevice *device = get_audio_device(devid);
     SDL_AudioStatus status = SDL_AUDIO_STOPPED;
-    if (device && device->enabled) {
-        if (device->paused) {
+    if (device && SDL_AtomicGet(&device->enabled)) {
+        if (SDL_AtomicGet(&device->paused)) {
             status = SDL_AUDIO_PAUSED;
         } else {
             status = SDL_AUDIO_PLAYING;
@@ -1281,7 +1500,7 @@ SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
     SDL_AudioDevice *device = get_audio_device(devid);
     if (device) {
         current_audio.impl.LockDevice(device);
-        device->paused = pause_on;
+        SDL_AtomicSet(&device->paused, pause_on ? 1 : 0);
         current_audio.impl.UnlockDevice(device);
     }
 }
@@ -1328,11 +1547,7 @@ SDL_UnlockAudio(void)
 void
 SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
 {
-    SDL_AudioDevice *device = get_audio_device(devid);
-    if (device) {
-        close_audio_device(device);
-        open_devices[devid - 1] = NULL;
-    }
+    close_audio_device(get_audio_device(devid));
 }
 
 void
@@ -1351,9 +1566,7 @@ SDL_AudioQuit(void)
     }
 
     for (i = 0; i < SDL_arraysize(open_devices); i++) {
-        if (open_devices[i] != NULL) {
-            SDL_CloseAudioDevice(i+1);
-        }
+        close_audio_device(open_devices[i]);
     }
 
     free_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount);

+ 0 - 3
Engine/lib/sdl/src/audio/SDL_audio_c.h

@@ -29,9 +29,6 @@ extern SDL_AudioFormat SDL_NextAudioFormat(void);
 /* Function to calculate the size and silence for a SDL_AudioSpec */
 extern void SDL_CalculateAudioSpec(SDL_AudioSpec * spec);
 
-/* The actual mixing thread function */
-extern int SDLCALL SDL_RunAudio(void *audiop);
-
 /* this is used internally to access some autogenerated code. */
 typedef struct
 {

+ 49 - 1
Engine/lib/sdl/src/audio/SDL_mixer.c

@@ -202,6 +202,54 @@ SDL_MixAudioFormat(Uint8 * dst, const Uint8 * src, SDL_AudioFormat format,
         }
         break;
 
+    case AUDIO_U16LSB:
+        {
+            Uint16 src1, src2;
+            int dst_sample;
+            const int max_audioval = 0xFFFF;
+
+            len /= 2;
+            while (len--) {
+                src1 = ((src[1]) << 8 | src[0]);
+                ADJUST_VOLUME(src1, volume);
+                src2 = ((dst[1]) << 8 | dst[0]);
+                src += 2;
+                dst_sample = src1 + src2;
+                if (dst_sample > max_audioval) {
+                    dst_sample = max_audioval;
+                }
+                dst[0] = dst_sample & 0xFF;
+                dst_sample >>= 8;
+                dst[1] = dst_sample & 0xFF;
+                dst += 2;
+            }
+        }
+        break;
+
+    case AUDIO_U16MSB:
+        {
+            Uint16 src1, src2;
+            int dst_sample;
+            const int max_audioval = 0xFFFF;
+
+            len /= 2;
+            while (len--) {
+                src1 = ((src[0]) << 8 | src[1]);
+                ADJUST_VOLUME(src1, volume);
+                src2 = ((dst[0]) << 8 | dst[1]);
+                src += 2;
+                dst_sample = src1 + src2;
+                if (dst_sample > max_audioval) {
+                    dst_sample = max_audioval;
+                }
+                dst[1] = dst_sample & 0xFF;
+                dst_sample >>= 8;
+                dst[0] = dst_sample & 0xFF;
+                dst += 2;
+            }
+        }
+        break;
+
     case AUDIO_S32LSB:
         {
             const Uint32 *src32 = (Uint32 *) src;
@@ -313,7 +361,7 @@ SDL_MixAudioFormat(Uint8 * dst, const Uint8 * src, SDL_AudioFormat format,
         break;
 
     default:                   /* If this happens... FIXME! */
-        SDL_SetError("SDL_MixAudio(): unknown audio format");
+        SDL_SetError("SDL_MixAudioFormat(): unknown audio format");
         return;
     }
 }

+ 15 - 9
Engine/lib/sdl/src/audio/SDL_sysaudio.h

@@ -26,6 +26,10 @@
 #include "SDL_mutex.h"
 #include "SDL_thread.h"
 
+/* !!! FIXME: These are wordy and unlocalized... */
+#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
+#define DEFAULT_INPUT_DEVNAME "System audio capture device"
+
 /* The SDL audio driver */
 typedef struct SDL_AudioDevice SDL_AudioDevice;
 #define _THIS   SDL_AudioDevice *_this
@@ -75,7 +79,9 @@ typedef struct SDL_AudioDriverImpl
     void (*PlayDevice) (_THIS);
     int (*GetPendingBytes) (_THIS);
     Uint8 *(*GetDeviceBuf) (_THIS);
-    void (*WaitDone) (_THIS);
+    int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
+    void (*FlushCapture) (_THIS);
+    void (*PrepareToClose) (_THIS);  /**< Called between run and draining wait for playback devices */
     void (*CloseDevice) (_THIS);
     void (*LockDevice) (_THIS);
     void (*UnlockDevice) (_THIS);
@@ -87,10 +93,10 @@ typedef struct SDL_AudioDriverImpl
     /* Some flags to push duplicate code into the core and reduce #ifdefs. */
     /* !!! FIXME: these should be SDL_bool */
     int ProvidesOwnCallbackThread;
-    int SkipMixerLock;  /* !!! FIXME: do we need this anymore? */
+    int SkipMixerLock;
     int HasCaptureSupport;
     int OnlyHasDefaultOutputDevice;
-    int OnlyHasDefaultInputDevice;
+    int OnlyHasDefaultCaptureDevice;
     int AllowsArbitraryDeviceNames;
 } SDL_AudioDriverImpl;
 
@@ -157,12 +163,10 @@ struct SDL_AudioDevice
     SDL_AudioStreamer streamer;
 
     /* Current state flags */
-    /* !!! FIXME: should be SDL_bool */
-    int iscapture;
-    int enabled;  /* true if device is functioning and connected. */
-    int shutdown; /* true if we are signaling the play thread to end. */
-    int paused;
-    int opened;
+    SDL_atomic_t shutdown; /* true if we are signaling the play thread to end. */
+    SDL_atomic_t enabled;  /* true if device is functioning and connected. */
+    SDL_atomic_t paused;
+    SDL_bool iscapture;
 
     /* Fake audio buffer for when the audio hardware is busy */
     Uint8 *fake_stream;
@@ -183,6 +187,8 @@ struct SDL_AudioDevice
     /* * * */
     /* Data private to this driver */
     struct SDL_PrivateAudioData *hidden;
+
+    void *handle;
 };
 #undef _THIS
 

+ 1 - 1
Engine/lib/sdl/src/audio/SDL_wave.c

@@ -504,7 +504,7 @@ SDL_LoadWAV_RW(SDL_RWops * src, int freesrc,
         was_error = 1;
         goto done;
     }
-    SDL_memset(spec, 0, (sizeof *spec));
+    SDL_zerop(spec);
     spec->freq = SDL_SwapLE32(format->frequency);
 
     if (IEEE_float_encoded) {

+ 374 - 76
Engine/lib/sdl/src/audio/alsa/SDL_alsa_audio.c

@@ -29,9 +29,9 @@
 #include <errno.h>
 #include <string.h>
 
+#include "SDL_assert.h"
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_alsa_audio.h"
 
@@ -42,8 +42,10 @@
 static int (*ALSA_snd_pcm_open)
   (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
-static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
+static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)
   (snd_pcm_t *, const void *, snd_pcm_uframes_t);
+static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)
+  (snd_pcm_t *, void *, snd_pcm_uframes_t);
 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
@@ -85,6 +87,10 @@ static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
 static int (*ALSA_snd_pcm_sw_params_set_avail_min)
   (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
+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 **);
 
 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
@@ -118,6 +124,7 @@ load_alsa_syms(void)
     SDL_ALSA_SYM(snd_pcm_open);
     SDL_ALSA_SYM(snd_pcm_close);
     SDL_ALSA_SYM(snd_pcm_writei);
+    SDL_ALSA_SYM(snd_pcm_readi);
     SDL_ALSA_SYM(snd_pcm_recover);
     SDL_ALSA_SYM(snd_pcm_prepare);
     SDL_ALSA_SYM(snd_pcm_drain);
@@ -144,6 +151,11 @@ load_alsa_syms(void)
     SDL_ALSA_SYM(snd_pcm_nonblock);
     SDL_ALSA_SYM(snd_pcm_wait);
     SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
+    SDL_ALSA_SYM(snd_pcm_reset);
+    SDL_ALSA_SYM(snd_device_name_hint);
+    SDL_ALSA_SYM(snd_device_name_get_hint);
+    SDL_ALSA_SYM(snd_device_name_free_hint);
+
     return 0;
 }
 
@@ -196,25 +208,27 @@ LoadALSALibrary(void)
 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
 
 static const char *
-get_audio_device(int channels)
+get_audio_device(void *handle, const int channels)
 {
     const char *device;
 
+    if (handle != NULL) {
+        return (const char *) handle;
+    }
+
+    /* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
     device = SDL_getenv("AUDIODEV");    /* Is there a standard variable name? */
-    if (device == NULL) {
-        switch (channels) {
-        case 6:
-            device = "plug:surround51";
-            break;
-        case 4:
-            device = "plug:surround40";
-            break;
-        default:
-            device = "default";
-            break;
-        }
+    if (device != NULL) {
+        return device;
+    }
+
+    if (channels == 6) {
+        return "plug:surround51";
+    } else if (channels == 4) {
+        return "plug:surround40";
     }
-    return device;
+
+    return "default";
 }
 
 
@@ -232,37 +246,37 @@ ALSA_WaitDevice(_THIS)
  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
  */
-#define SWIZ6(T) \
-    T *ptr = (T *) this->hidden->mixbuf; \
+#define SWIZ6(T, buf, numframes) \
+    T *ptr = (T *) buf; \
     Uint32 i; \
-    for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
+    for (i = 0; i < numframes; i++, ptr += 6) { \
         T tmp; \
         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
     }
 
 static SDL_INLINE void
-swizzle_alsa_channels_6_64bit(_THIS)
+swizzle_alsa_channels_6_64bit(void *buffer, Uint32 bufferlen)
 {
-    SWIZ6(Uint64);
+    SWIZ6(Uint64, buffer, bufferlen);
 }
 
 static SDL_INLINE void
-swizzle_alsa_channels_6_32bit(_THIS)
+swizzle_alsa_channels_6_32bit(void *buffer, Uint32 bufferlen)
 {
-    SWIZ6(Uint32);
+    SWIZ6(Uint32, buffer, bufferlen);
 }
 
 static SDL_INLINE void
-swizzle_alsa_channels_6_16bit(_THIS)
+swizzle_alsa_channels_6_16bit(void *buffer, Uint32 bufferlen)
 {
-    SWIZ6(Uint16);
+    SWIZ6(Uint16, buffer, bufferlen);
 }
 
 static SDL_INLINE void
-swizzle_alsa_channels_6_8bit(_THIS)
+swizzle_alsa_channels_6_8bit(void *buffer, Uint32 bufferlen)
 {
-    SWIZ6(Uint8);
+    SWIZ6(Uint8, buffer, bufferlen);
 }
 
 #undef SWIZ6
@@ -273,18 +287,16 @@ swizzle_alsa_channels_6_8bit(_THIS)
  *  channels from Windows/Mac order to the format alsalib will want.
  */
 static SDL_INLINE void
-swizzle_alsa_channels(_THIS)
+swizzle_alsa_channels(_THIS, void *buffer, Uint32 bufferlen)
 {
     if (this->spec.channels == 6) {
-        const Uint16 fmtsize = (this->spec.format & 0xFF);      /* bits/channel. */
-        if (fmtsize == 16)
-            swizzle_alsa_channels_6_16bit(this);
-        else if (fmtsize == 8)
-            swizzle_alsa_channels_6_8bit(this);
-        else if (fmtsize == 32)
-            swizzle_alsa_channels_6_32bit(this);
-        else if (fmtsize == 64)
-            swizzle_alsa_channels_6_64bit(this);
+        switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
+            case 8: swizzle_alsa_channels_6_8bit(buffer, bufferlen); break;
+            case 16: swizzle_alsa_channels_6_16bit(buffer, bufferlen); break;
+            case 32: swizzle_alsa_channels_6_32bit(buffer, bufferlen); break;
+            case 64: swizzle_alsa_channels_6_64bit(buffer, bufferlen); break;
+            default: SDL_assert(!"unhandled bitsize"); break;
+        }
     }
 
     /* !!! FIXME: update this for 7.1 if needed, later. */
@@ -294,19 +306,29 @@ swizzle_alsa_channels(_THIS)
 static void
 ALSA_PlayDevice(_THIS)
 {
-    int status;
     const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
-    const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) *
+    const int frame_size = (((int) SDL_AUDIO_BITSIZE(this->spec.format)) / 8) *
                                 this->spec.channels;
     snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
 
-    swizzle_alsa_channels(this);
+    swizzle_alsa_channels(this, this->hidden->mixbuf, frames_left);
+
+    while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
+        int status;
+
+        /* This wait is a work-around for a hang when USB devices are
+           unplugged.  Normally it should not result in any waiting,
+           but in the case of a USB unplug, it serves as a way to
+           join the playback thread after the timeout occurs */
+        status = ALSA_snd_pcm_wait(this->hidden->pcm_handle, 1000);
+        if (status == 0) {
+            /*fprintf(stderr, "ALSA timeout waiting for available buffer space\n");*/
+            SDL_OpenedAudioDeviceDisconnected(this);
+            return;
+        }
 
-    while ( frames_left > 0 && this->enabled ) {
-        /* !!! FIXME: This works, but needs more testing before going live */
-        /* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
         status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
-                                     sample_buf, frames_left);
+                                         sample_buf, frames_left);
 
         if (status < 0) {
             if (status == -EAGAIN) {
@@ -336,20 +358,71 @@ ALSA_GetDeviceBuf(_THIS)
     return (this->hidden->mixbuf);
 }
 
+static int
+ALSA_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    Uint8 *sample_buf = (Uint8 *) buffer;
+    const int frame_size = (((int) SDL_AUDIO_BITSIZE(this->spec.format)) / 8) *
+                                this->spec.channels;
+    const int total_frames = buflen / frame_size;
+    snd_pcm_uframes_t frames_left = total_frames;
+
+    SDL_assert((buflen % frame_size) == 0);
+
+    while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
+        /* !!! FIXME: This works, but needs more testing before going live */
+        /* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
+        int status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
+                                        sample_buf, frames_left);
+
+        if (status < 0) {
+            /*printf("ALSA: capture error %d\n", status);*/
+            if (status == -EAGAIN) {
+                /* Apparently snd_pcm_recover() doesn't handle this case -
+                   does it assume snd_pcm_wait() above? */
+                SDL_Delay(1);
+                continue;
+            }
+            status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
+            if (status < 0) {
+                /* Hmm, not much we can do - abort */
+                fprintf(stderr, "ALSA read failed (unrecoverable): %s\n",
+                        ALSA_snd_strerror(status));
+                return -1;
+            }
+            continue;
+        }
+
+        /*printf("ALSA: captured %d bytes\n", status * frame_size);*/
+        sample_buf += status * frame_size;
+        frames_left -= status;
+    }
+
+    swizzle_alsa_channels(this, buffer, total_frames - frames_left);
+
+    return (total_frames - frames_left) * frame_size;
+}
+
+static void
+ALSA_FlushCapture(_THIS)
+{
+    ALSA_snd_pcm_reset(this->hidden->pcm_handle);
+}
+
 static void
 ALSA_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->pcm_handle) {
-            ALSA_snd_pcm_drain(this->hidden->pcm_handle);
-            ALSA_snd_pcm_close(this->hidden->pcm_handle);
-            this->hidden->pcm_handle = NULL;
-        }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->pcm_handle) {
+	/* 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;
+        SDL_Delay(delay);
+
+        ALSA_snd_pcm_close(this->hidden->pcm_handle);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -482,16 +555,16 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Open the audio device */
     /* Name of device should depend on # channels in spec */
     status = ALSA_snd_pcm_open(&pcm_handle,
-                               get_audio_device(this->spec.channels),
-                               SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+                get_audio_device(handle, this->spec.channels),
+                iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+                SND_PCM_NONBLOCK);
 
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't open audio device: %s",
                             ALSA_snd_strerror(status));
     }
@@ -502,7 +575,6 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     snd_pcm_hw_params_alloca(&hwparams);
     status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't get hardware config: %s",
                             ALSA_snd_strerror(status));
     }
@@ -511,7 +583,6 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
                                                SND_PCM_ACCESS_RW_INTERLEAVED);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't set interleaved access: %s",
                      ALSA_snd_strerror(status));
     }
@@ -565,7 +636,6 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         }
     }
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
@@ -577,7 +647,6 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (status < 0) {
         status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
         if (status < 0) {
-            ALSA_CloseDevice(this);
             return SDL_SetError("ALSA: Couldn't set audio channels");
         }
         this->spec.channels = channels;
@@ -588,7 +657,6 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
                                                   &rate, NULL);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't set audio frequency: %s",
                             ALSA_snd_strerror(status));
     }
@@ -598,8 +666,8 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     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... */
-        if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
-            ALSA_CloseDevice(this);
+        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));
         }
     }
@@ -607,26 +675,22 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     snd_pcm_sw_params_alloca(&swparams);
     status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't get software config: %s",
                             ALSA_snd_strerror(status));
     }
     status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("Couldn't set minimum available samples: %s",
                             ALSA_snd_strerror(status));
     }
     status =
         ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("ALSA: Couldn't set start threshold: %s",
                             ALSA_snd_strerror(status));
     }
     status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
     if (status < 0) {
-        ALSA_CloseDevice(this);
         return SDL_SetError("Couldn't set software audio parameters: %s",
                             ALSA_snd_strerror(status));
     }
@@ -635,13 +699,14 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     SDL_CalculateAudioSpec(&this->spec);
 
     /* Allocate mixing buffer */
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        ALSA_CloseDevice(this);
-        return SDL_OutOfMemory();
+    if (!iscapture) {
+        this->hidden->mixlen = this->spec.size;
+        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
+        if (this->hidden->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
 
     /* Switch to blocking mode for playback */
     ALSA_snd_pcm_nonblock(pcm_handle, 0);
@@ -650,9 +715,238 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     return 0;
 }
 
+typedef struct ALSA_Device
+{
+    char *name;
+    SDL_bool iscapture;
+    struct ALSA_Device *next;
+} ALSA_Device;
+
+static void
+add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
+{
+    ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
+    char *desc = ALSA_snd_device_name_get_hint(hint, "DESC");
+    char *handle = NULL;
+    char *ptr;
+
+    if (!desc) {
+        SDL_free(dev);
+        return;
+    } else if (!dev) {
+        free(desc);
+        return;
+    }
+
+    SDL_assert(name != NULL);
+
+    /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
+       just chop the extra lines off, this seems to get a reasonable device
+       name without extra details. */
+    if ((ptr = strchr(desc, '\n')) != NULL) {
+        *ptr = '\0';
+    }
+
+    /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
+
+    handle = SDL_strdup(name);
+    if (!handle) {
+        free(desc);
+        SDL_free(dev);
+        return;
+    }
+
+    SDL_AddAudioDevice(iscapture, desc, handle);
+    free(desc);
+
+    dev->name = handle;
+    dev->iscapture = iscapture;
+    dev->next = *pSeen;
+    *pSeen = dev;
+}
+
+
+static SDL_atomic_t ALSA_hotplug_shutdown;
+static SDL_Thread *ALSA_hotplug_thread;
+
+static int SDLCALL
+ALSA_HotplugThread(void *arg)
+{
+    SDL_sem *first_run_semaphore = (SDL_sem *) arg;
+    ALSA_Device *devices = NULL;
+    ALSA_Device *next;
+    ALSA_Device *dev;
+    Uint32 ticks;
+
+    while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
+        void **hints = NULL;
+        if (ALSA_snd_device_name_hint(-1, "pcm", &hints) != -1) {
+            ALSA_Device *unseen = devices;
+            ALSA_Device *seen = NULL;
+            ALSA_Device *prev;
+            int i, j;
+            const char *match = NULL;
+            int bestmatch = 0xFFFF;
+            size_t match_len = 0;
+            int defaultdev = -1;
+            static const char * const prefixes[] = {
+                "hw:", "sysdefault:", "default:", NULL
+            };
+
+            /* Apparently there are several different ways that ALSA lists
+               actual hardware. It could be prefixed with "hw:" or "default:"
+               or "sysdefault:" and maybe others. Go through the list and see
+               if we can find a preferred prefix for the system. */
+            for (i = 0; hints[i]; i++) {
+                char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
+                if (!name) {
+                    continue;
+                }
+
+                /* full name, not a prefix */
+                if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) {
+                    defaultdev = i;
+                }
+
+                for (j = 0; prefixes[j]; j++) {
+                    const char *prefix = prefixes[j];
+                    const size_t prefixlen = SDL_strlen(prefix);
+                    if (SDL_strncmp(name, prefix, prefixlen) == 0) {
+                        if (j < bestmatch) {
+                            bestmatch = j;
+                            match = prefix;
+                            match_len = prefixlen;
+                        }
+                    }
+                }
+
+                free(name);
+            }
+
+            /* look through the list of device names to find matches */
+            for (i = 0; hints[i]; i++) {
+                char *name;
+
+                /* if we didn't find a device name prefix we like at all... */
+                if ((!match) && (defaultdev != i)) {
+                    continue;  /* ...skip anything that isn't the default device. */
+                }
+
+                name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
+                if (!name) {
+                    continue;
+                }
+
+                /* only want physical hardware interfaces */
+                if (!match || (SDL_strncmp(name, match, match_len) == 0)) {
+                    char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
+                    const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
+                    const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
+                    SDL_bool have_output = SDL_FALSE;
+                    SDL_bool have_input = SDL_FALSE;
+
+                    free(ioid);
+
+                    if (!isoutput && !isinput) {
+                        free(name);
+                        continue;
+                    }
+
+                    prev = NULL;
+                    for (dev = unseen; dev; dev = next) {
+                        next = dev->next;
+                        if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
+                            if (prev) {
+                                prev->next = next;
+                            } else {
+                                unseen = next;
+                            }
+                            dev->next = seen;
+                            seen = dev;
+                            if (isinput) have_input = SDL_TRUE;
+                            if (isoutput) have_output = SDL_TRUE;
+                        } else {
+                            prev = dev;
+                        }
+                    }
+
+                    if (isinput && !have_input) {
+                        add_device(SDL_TRUE, name, hints[i], &seen);
+                    }
+                    if (isoutput && !have_output) {
+                        add_device(SDL_FALSE, name, hints[i], &seen);
+                    }
+                }
+
+                free(name);
+            }
+
+            ALSA_snd_device_name_free_hint(hints);
+
+            devices = seen;   /* now we have a known-good list of attached devices. */
+
+            /* report anything still in unseen as removed. */
+            for (dev = unseen; dev; dev = next) {
+                /*printf("ALSA: removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
+                next = dev->next;
+                SDL_RemoveAudioDevice(dev->iscapture, dev->name);
+                SDL_free(dev->name);
+                SDL_free(dev);
+            }
+        }
+
+        /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
+        if (first_run_semaphore) {
+            SDL_SemPost(first_run_semaphore);
+            first_run_semaphore = NULL;  /* let other thread clean it up. */
+        }
+
+        /* Block awhile before checking again, unless we're told to stop. */
+        ticks = SDL_GetTicks() + 5000;
+        while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
+            SDL_Delay(100);
+        }
+    }
+
+    /* Shutting down! Clean up any data we've gathered. */
+    for (dev = devices; dev; dev = next) {
+        /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
+        next = dev->next;
+        SDL_free(dev->name);
+        SDL_free(dev);
+    }
+
+    return 0;
+}
+
+static void
+ALSA_DetectDevices(void)
+{
+    /* Start the device detection thread here, wait for an initial iteration to complete. */
+    SDL_sem *semaphore = SDL_CreateSemaphore(0);
+    if (!semaphore) {
+        return;  /* oh well. */
+    }
+
+    SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
+
+    ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
+    if (ALSA_hotplug_thread) {
+        SDL_SemWait(semaphore);  /* wait for the first iteration to finish. */
+    }
+
+    SDL_DestroySemaphore(semaphore);
+}
+
 static void
 ALSA_Deinitialize(void)
 {
+    if (ALSA_hotplug_thread != NULL) {
+        SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
+        SDL_WaitThread(ALSA_hotplug_thread, NULL);
+        ALSA_hotplug_thread = NULL;
+    }
+
     UnloadALSALibrary();
 }
 
@@ -664,13 +958,17 @@ ALSA_Init(SDL_AudioDriverImpl * impl)
     }
 
     /* Set the function pointers */
+    impl->DetectDevices = ALSA_DetectDevices;
     impl->OpenDevice = ALSA_OpenDevice;
     impl->WaitDevice = ALSA_WaitDevice;
     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
     impl->PlayDevice = ALSA_PlayDevice;
     impl->CloseDevice = ALSA_CloseDevice;
     impl->Deinitialize = ALSA_Deinitialize;
-    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: Add device enum! */
+    impl->CaptureFromDevice = ALSA_CaptureFromDevice;
+    impl->FlushCapture = ALSA_FlushCapture;
+
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }

+ 73 - 36
Engine/lib/sdl/src/audio/android/SDL_androidaudio.c

@@ -24,6 +24,7 @@
 
 /* Output audio to Android */
 
+#include "SDL_assert.h"
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
 #include "SDL_androidaudio.h"
@@ -33,23 +34,22 @@
 #include <android/log.h>
 
 static SDL_AudioDevice* audioDevice = NULL;
+static SDL_AudioDevice* captureDevice = NULL;
 
 static int
-AndroidAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
     SDL_AudioFormat test_format;
 
-    if (iscapture) {
-        /* TODO: implement capture */
-        return SDL_SetError("Capture not supported on Android");
-    }
+    SDL_assert((captureDevice == NULL) || !iscapture);
+    SDL_assert((audioDevice == NULL) || iscapture);
 
-    if (audioDevice != NULL) {
-        return SDL_SetError("Only one audio device at a time please!");
+    if (iscapture) {
+        captureDevice = this;
+    } else {
+        audioDevice = this;
     }
 
-    audioDevice = this;
-    
     this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof *this->hidden));
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
@@ -82,100 +82,137 @@ AndroidAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         this->spec.freq = 48000;
     }
 
-    /* TODO: pass in/return a (Java) device ID, also whether we're opening for input or output */
-    this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
-    SDL_CalculateAudioSpec(&this->spec);
+    /* TODO: pass in/return a (Java) device ID */
+    this->spec.samples = Android_JNI_OpenAudioDevice(iscapture, this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
 
     if (this->spec.samples == 0) {
         /* Init failed? */
         return SDL_SetError("Java-side initialization failed!");
     }
 
+    SDL_CalculateAudioSpec(&this->spec);
+
     return 0;
 }
 
 static void
-AndroidAUD_PlayDevice(_THIS)
+ANDROIDAUDIO_PlayDevice(_THIS)
 {
     Android_JNI_WriteAudioBuffer();
 }
 
 static Uint8 *
-AndroidAUD_GetDeviceBuf(_THIS)
+ANDROIDAUDIO_GetDeviceBuf(_THIS)
 {
     return Android_JNI_GetAudioBuffer();
 }
 
+static int
+ANDROIDAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    return Android_JNI_CaptureAudioBuffer(buffer, buflen);
+}
+
+static void
+ANDROIDAUDIO_FlushCapture(_THIS)
+{
+    Android_JNI_FlushCapturedAudio();
+}
+
 static void
-AndroidAUD_CloseDevice(_THIS)
+ANDROIDAUDIO_CloseDevice(_THIS)
 {
     /* At this point SDL_CloseAudioDevice via close_audio_device took care of terminating the audio thread
        so it's safe to terminate the Java side buffer and AudioTrack
      */
-    Android_JNI_CloseAudioDevice();
-
-    if (audioDevice == this) {
-        if (audioDevice->hidden != NULL) {
-            SDL_free(this->hidden);
-            this->hidden = NULL;
-        }
+    Android_JNI_CloseAudioDevice(this->iscapture);
+    if (this->iscapture) {
+        SDL_assert(captureDevice == this);
+        captureDevice = NULL;
+    } else {
+        SDL_assert(audioDevice == this);
         audioDevice = NULL;
     }
+    SDL_free(this->hidden);
 }
 
 static int
-AndroidAUD_Init(SDL_AudioDriverImpl * impl)
+ANDROIDAUDIO_Init(SDL_AudioDriverImpl * impl)
 {
     /* Set the function pointers */
-    impl->OpenDevice = AndroidAUD_OpenDevice;
-    impl->PlayDevice = AndroidAUD_PlayDevice;
-    impl->GetDeviceBuf = AndroidAUD_GetDeviceBuf;
-    impl->CloseDevice = AndroidAUD_CloseDevice;
+    impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
+    impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
+    impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
+    impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
+    impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
+    impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
 
     /* and the capabilities */
-    impl->HasCaptureSupport = 0; /* TODO */
+    impl->HasCaptureSupport = SDL_TRUE;
     impl->OnlyHasDefaultOutputDevice = 1;
-    impl->OnlyHasDefaultInputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
 
     return 1;   /* this audio target is available. */
 }
 
-AudioBootStrap ANDROIDAUD_bootstrap = {
-    "android", "SDL Android audio driver", AndroidAUD_Init, 0
+AudioBootStrap ANDROIDAUDIO_bootstrap = {
+    "android", "SDL Android audio driver", ANDROIDAUDIO_Init, 0
 };
 
 /* Pause (block) all non already paused audio devices by taking their mixer lock */
-void AndroidAUD_PauseDevices(void)
+void ANDROIDAUDIO_PauseDevices(void)
 {
     /* TODO: Handle multiple devices? */
     struct SDL_PrivateAudioData *private;
     if(audioDevice != NULL && audioDevice->hidden != NULL) {
         private = (struct SDL_PrivateAudioData *) audioDevice->hidden;
-        if (audioDevice->paused) {
+        if (SDL_AtomicGet(&audioDevice->paused)) {
             /* The device is already paused, leave it alone */
             private->resume = SDL_FALSE;
         }
         else {
             SDL_LockMutex(audioDevice->mixer_lock);
-            audioDevice->paused = SDL_TRUE;
+            SDL_AtomicSet(&audioDevice->paused, 1);
+            private->resume = SDL_TRUE;
+        }
+    }
+
+    if(captureDevice != NULL && captureDevice->hidden != NULL) {
+        private = (struct SDL_PrivateAudioData *) captureDevice->hidden;
+        if (SDL_AtomicGet(&captureDevice->paused)) {
+            /* The device is already paused, leave it alone */
+            private->resume = SDL_FALSE;
+        }
+        else {
+            SDL_LockMutex(captureDevice->mixer_lock);
+            SDL_AtomicSet(&captureDevice->paused, 1);
             private->resume = SDL_TRUE;
         }
     }
 }
 
 /* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
-void AndroidAUD_ResumeDevices(void)
+void ANDROIDAUDIO_ResumeDevices(void)
 {
     /* TODO: Handle multiple devices? */
     struct SDL_PrivateAudioData *private;
     if(audioDevice != NULL && audioDevice->hidden != NULL) {
         private = (struct SDL_PrivateAudioData *) audioDevice->hidden;
         if (private->resume) {
-            audioDevice->paused = SDL_FALSE;
+            SDL_AtomicSet(&audioDevice->paused, 0);
             private->resume = SDL_FALSE;
             SDL_UnlockMutex(audioDevice->mixer_lock);
         }
     }
+
+    if(captureDevice != NULL && captureDevice->hidden != NULL) {
+        private = (struct SDL_PrivateAudioData *) captureDevice->hidden;
+        if (private->resume) {
+            SDL_AtomicSet(&captureDevice->paused, 0);
+            private->resume = SDL_FALSE;
+            SDL_UnlockMutex(captureDevice->mixer_lock);
+        }
+    }
 }
 
 

+ 0 - 2
Engine/lib/sdl/src/audio/android/SDL_androidaudio.h

@@ -34,8 +34,6 @@ struct SDL_PrivateAudioData
     int resume;
 };
 
-static void AndroidAUD_CloseDevice(_THIS);
-
 #endif /* _SDL_androidaudio_h */
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 7 - 26
Engine/lib/sdl/src/audio/arts/SDL_artsaudio.c

@@ -32,7 +32,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_artsaudio.h"
 
@@ -186,13 +185,6 @@ ARTS_PlayDevice(_THIS)
 #endif
 }
 
-static void
-ARTS_WaitDone(_THIS)
-{
-    /* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
-}
-
-
 static Uint8 *
 ARTS_GetDeviceBuf(_THIS)
 {
@@ -203,17 +195,12 @@ ARTS_GetDeviceBuf(_THIS)
 static void
 ARTS_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->stream) {
-            SDL_NAME(arts_close_stream) (this->hidden->stream);
-            this->hidden->stream = 0;
-        }
-        SDL_NAME(arts_free) ();
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->stream) {
+        SDL_NAME(arts_close_stream) (this->hidden->stream);
     }
+    SDL_NAME(arts_free) ();
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -241,7 +228,7 @@ ARTS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Try for a closest match on audio format */
     for (test_format = SDL_FirstAudioFormat(this->spec.format);
@@ -267,19 +254,16 @@ ARTS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         }
     }
     if (format == 0) {
-        ARTS_CloseDevice(this);
         return SDL_SetError("Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
 
     if ((rc = SDL_NAME(arts_init) ()) != 0) {
-        ARTS_CloseDevice(this);
         return SDL_SetError("Unable to initialize ARTS: %s",
                             SDL_NAME(arts_error_text) (rc));
     }
 
     if (!ARTS_Suspend()) {
-        ARTS_CloseDevice(this);
         return SDL_SetError("ARTS can not open audio device");
     }
 
@@ -297,7 +281,6 @@ ARTS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Determine the power of two of the fragment size */
     for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec);
     if ((0x01 << frag_spec) != this->spec.size) {
-        ARTS_CloseDevice(this);
         return SDL_SetError("Fragment size must be a power of two");
     }
     frag_spec |= 0x00020000;    /* two fragments, for low latency */
@@ -316,9 +299,8 @@ ARTS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     /* Allocate mixing buffer */
     this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
+    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        ARTS_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
@@ -367,7 +349,6 @@ ARTS_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = ARTS_WaitDevice;
     impl->GetDeviceBuf = ARTS_GetDeviceBuf;
     impl->CloseDevice = ARTS_CloseDevice;
-    impl->WaitDone = ARTS_WaitDone;
     impl->Deinitialize = ARTS_Deinitialize;
     impl->OnlyHasDefaultOutputDevice = 1;
 

+ 110 - 55
Engine/lib/sdl/src/audio/bsd/SDL_bsdaudio.c

@@ -38,7 +38,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audiodev_c.h"
 #include "SDL_bsdaudio.h"
@@ -63,13 +62,17 @@ BSDAUDIO_Status(_THIS)
 #ifdef DEBUG_AUDIO
     /* *INDENT-OFF* */
     audio_info_t info;
+    const audio_prinfo *prinfo;
 
     if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
         fprintf(stderr, "AUDIO_GETINFO failed.\n");
         return;
     }
+
+    prinfo = this->iscapture ? &info.play : &info.record;
+
     fprintf(stderr, "\n"
-            "[play/record info]\n"
+            "[%s info]\n"
             "buffer size	:   %d bytes\n"
             "sample rate	:   %i Hz\n"
             "channels	:   %i\n"
@@ -83,18 +86,19 @@ BSDAUDIO_Status(_THIS)
             "waiting		:   %s\n"
             "active		:   %s\n"
             "",
-            info.play.buffer_size,
-            info.play.sample_rate,
-            info.play.channels,
-            info.play.precision,
-            info.play.encoding,
-            info.play.seek,
-            info.play.samples,
-            info.play.eof,
-            info.play.pause ? "yes" : "no",
-            info.play.error ? "yes" : "no",
-            info.play.waiting ? "yes" : "no",
-            info.play.active ? "yes" : "no");
+            this->iscapture ? "record" : "play",
+            prinfo->buffer_size,
+            prinfo->sample_rate,
+            prinfo->channels,
+            prinfo->precision,
+            prinfo->encoding,
+            prinfo->seek,
+            prinfo->samples,
+            prinfo->eof,
+            prinfo->pause ? "yes" : "no",
+            prinfo->error ? "yes" : "no",
+            prinfo->waiting ? "yes" : "no",
+            prinfo->active ? "yes" : "no");
 
     fprintf(stderr, "\n"
             "[audio info]\n"
@@ -182,11 +186,15 @@ BSDAUDIO_PlayDevice(_THIS)
             break;
         }
 
-        if (p < written
+#ifdef DEBUG_AUDIO
+        fprintf(stderr, "Wrote %d bytes of audio data\n", written);
+#endif
+
+        if (p < this->hidden->mixlen
             || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
             SDL_Delay(1);       /* Let a little CPU time go by */
         }
-    } while (p < written);
+    } while (p < this->hidden->mixlen);
 
     /* If timer synchronization is enabled, set the next write frame */
     if (this->hidden->frame_ticks) {
@@ -197,9 +205,6 @@ BSDAUDIO_PlayDevice(_THIS)
     if (written < 0) {
         SDL_OpenedAudioDeviceDisconnected(this);
     }
-#ifdef DEBUG_AUDIO
-    fprintf(stderr, "Wrote %d bytes of audio data\n", written);
-#endif
 }
 
 static Uint8 *
@@ -208,27 +213,74 @@ BSDAUDIO_GetDeviceBuf(_THIS)
     return (this->hidden->mixbuf);
 }
 
+
+static int
+BSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
+{
+    Uint8 *buffer = (Uint8 *) _buffer;
+    int br, p = 0;
+
+    /* Write the audio data, checking for EAGAIN on broken audio drivers */
+    do {
+        br = read(this->hidden->audio_fd, buffer + p, buflen - p);
+        if (br > 0)
+            p += br;
+        if (br == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
+            /* Non recoverable error has occurred. It should be reported!!! */
+            perror("audio");
+            return p ? p : -1;
+        }
+
+#ifdef DEBUG_AUDIO
+        fprintf(stderr, "Captured %d bytes of audio data\n", br);
+#endif
+
+        if (p < buflen
+            || ((br < 0) && ((errno == 0) || (errno == EAGAIN)))) {
+            SDL_Delay(1);       /* Let a little CPU time go by */
+        }
+    } while (p < buflen);
+}
+
 static void
-BSDAUDIO_CloseDevice(_THIS)
+BSDAUDIO_FlushCapture(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->audio_fd >= 0) {
-            close(this->hidden->audio_fd);
-            this->hidden->audio_fd = -1;
+    audio_info_t info;
+    size_t remain;
+    Uint8 buf[512];
+
+    if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
+        return;  /* oh well. */
+    }
+
+    remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8));
+    while (remain > 0) {
+        const size_t len = SDL_min(sizeof (buf), remain);
+        const int br = read(this->hidden->audio_fd, buf, len);
+        if (br <= 0) {
+            return;  /* oh well. */
         }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+        remain -= br;
     }
 }
 
+static void
+BSDAUDIO_CloseDevice(_THIS)
+{
+    if (this->hidden->audio_fd >= 0) {
+        close(this->hidden->audio_fd);
+    }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
+}
+
 static int
 BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
-    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
+    const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
     SDL_AudioFormat format = 0;
     audio_info_t info;
+    audio_prinfo *prinfo = iscapture ? &info.play : &info.record;
 
     /* We don't care what the devname is...we'll try to open anything. */
     /*  ...but default to first name in the list... */
@@ -245,7 +297,7 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Open the audio device */
     this->hidden->audio_fd = open(devname, flags, 0);
@@ -259,9 +311,8 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     SDL_CalculateAudioSpec(&this->spec);
 
     /* Set to play mode */
-    info.mode = AUMODE_PLAY;
+    info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
     if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
-        BSDAUDIO_CloseDevice(this);
         return SDL_SetError("Couldn't put device into play mode");
     }
 
@@ -270,28 +321,28 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
          format; format = SDL_NextAudioFormat()) {
         switch (format) {
         case AUDIO_U8:
-            info.play.encoding = AUDIO_ENCODING_ULINEAR;
-            info.play.precision = 8;
+            prinfo->encoding = AUDIO_ENCODING_ULINEAR;
+            prinfo->precision = 8;
             break;
         case AUDIO_S8:
-            info.play.encoding = AUDIO_ENCODING_SLINEAR;
-            info.play.precision = 8;
+            prinfo->encoding = AUDIO_ENCODING_SLINEAR;
+            prinfo->precision = 8;
             break;
         case AUDIO_S16LSB:
-            info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
+            prinfo->precision = 16;
             break;
         case AUDIO_S16MSB:
-            info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
+            prinfo->precision = 16;
             break;
         case AUDIO_U16LSB:
-            info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
+            prinfo->precision = 16;
             break;
         case AUDIO_U16MSB:
-            info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
+            prinfo->precision = 16;
             break;
         default:
             continue;
@@ -303,33 +354,34 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (!format) {
-        BSDAUDIO_CloseDevice(this);
         return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
     }
 
     this->spec.format = format;
 
     AUDIO_INITINFO(&info);
-    info.play.channels = this->spec.channels;
+    prinfo->channels = this->spec.channels;
     if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
         this->spec.channels = 1;
     }
     AUDIO_INITINFO(&info);
-    info.play.sample_rate = this->spec.freq;
+    prinfo->sample_rate = this->spec.freq;
     info.blocksize = this->spec.size;
     info.hiwat = 5;
     info.lowat = 3;
     (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
     (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
-    this->spec.freq = info.play.sample_rate;
-    /* Allocate mixing buffer */
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        BSDAUDIO_CloseDevice(this);
-        return SDL_OutOfMemory();
+    this->spec.freq = prinfo->sample_rate;
+
+    if (!iscapture) {
+        /* Allocate mixing buffer */
+        this->hidden->mixlen = this->spec.size;
+        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
+        if (this->hidden->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
     BSDAUDIO_Status(this);
 
@@ -347,7 +399,10 @@ BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = BSDAUDIO_WaitDevice;
     impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
     impl->CloseDevice = BSDAUDIO_CloseDevice;
+    impl->CaptureFromDevice = BSDAUDIO_CaptureFromDevice;
+    impl->FlushCapture = BSDAUDIO_FlushCapture;
 
+    impl->HasCaptureSupport = SDL_TRUE;
     impl->AllowsArbitraryDeviceNames = 1;
 
     return 1;   /* this audio target is available. */

+ 13 - 3
Engine/lib/sdl/src/audio/coreaudio/SDL_coreaudio.h

@@ -33,9 +33,11 @@
 #include <CoreAudio/CoreAudio.h>
 #include <CoreServices/CoreServices.h>
 #else
-#include <AudioToolbox/AudioToolbox.h>
+#import <AVFoundation/AVFoundation.h>
+#import <UIKit/UIApplication.h>
 #endif
 
+#include <AudioToolbox/AudioToolbox.h>
 #include <AudioUnit/AudioUnit.h>
 
 /* Hidden "this" pointer for the audio functions */
@@ -43,13 +45,21 @@
 
 struct SDL_PrivateAudioData
 {
-    AudioUnit audioUnit;
-    int audioUnitOpened;
+    SDL_Thread *thread;
+    AudioQueueRef audioQueue;
+    AudioQueueBufferRef audioBuffer[2];
     void *buffer;
     UInt32 bufferOffset;
     UInt32 bufferSize;
+    AudioStreamBasicDescription strdesc;
+    SDL_sem *ready_semaphore;
+    char *thread_error;
+    SDL_atomic_t shutdown;
 #if MACOSX_COREAUDIO
     AudioDeviceID deviceID;
+#else
+    SDL_bool interrupted;
+    CFTypeRef interruption_listener;
 #endif
 };
 

+ 849 - 0
Engine/lib/sdl/src/audio/coreaudio/SDL_coreaudio.m

@@ -0,0 +1,849 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 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.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_AUDIO_DRIVER_COREAUDIO
+
+/* !!! FIXME: clean out some of the macro salsa in here. */
+
+#include "SDL_audio.h"
+#include "../SDL_audio_c.h"
+#include "../SDL_sysaudio.h"
+#include "SDL_coreaudio.h"
+#include "SDL_assert.h"
+#include "../../thread/SDL_systhread.h"
+
+#define DEBUG_COREAUDIO 0
+
+#define CHECK_RESULT(msg) \
+    if (result != noErr) { \
+        SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
+        return 0; \
+    }
+
+#if MACOSX_COREAUDIO
+static const AudioObjectPropertyAddress devlist_address = {
+    kAudioHardwarePropertyDevices,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+};
+
+typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
+
+typedef struct AudioDeviceList
+{
+    AudioDeviceID devid;
+    SDL_bool alive;
+    struct AudioDeviceList *next;
+} AudioDeviceList;
+
+static AudioDeviceList *output_devs = NULL;
+static AudioDeviceList *capture_devs = NULL;
+
+static SDL_bool
+add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
+{
+    AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
+    if (item == NULL) {
+        return SDL_FALSE;
+    }
+    item->devid = devId;
+    item->alive = SDL_TRUE;
+    item->next = iscapture ? capture_devs : output_devs;
+    if (iscapture) {
+        capture_devs = item;
+    } else {
+        output_devs = item;
+    }
+
+    return SDL_TRUE;
+}
+
+static void
+addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
+{
+    if (add_to_internal_dev_list(iscapture, devId)) {
+        SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
+    }
+}
+
+static void
+build_device_list(int iscapture, addDevFn addfn, void *addfndata)
+{
+    OSStatus result = noErr;
+    UInt32 size = 0;
+    AudioDeviceID *devs = NULL;
+    UInt32 i = 0;
+    UInt32 max = 0;
+
+    result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
+                                            &devlist_address, 0, NULL, &size);
+    if (result != kAudioHardwareNoError)
+        return;
+
+    devs = (AudioDeviceID *) alloca(size);
+    if (devs == NULL)
+        return;
+
+    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                        &devlist_address, 0, NULL, &size, devs);
+    if (result != kAudioHardwareNoError)
+        return;
+
+    max = size / sizeof (AudioDeviceID);
+    for (i = 0; i < max; i++) {
+        CFStringRef cfstr = NULL;
+        char *ptr = NULL;
+        AudioDeviceID dev = devs[i];
+        AudioBufferList *buflist = NULL;
+        int usable = 0;
+        CFIndex len = 0;
+        const AudioObjectPropertyAddress addr = {
+            kAudioDevicePropertyStreamConfiguration,
+            iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
+            kAudioObjectPropertyElementMaster
+        };
+
+        const AudioObjectPropertyAddress nameaddr = {
+            kAudioObjectPropertyName,
+            iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
+            kAudioObjectPropertyElementMaster
+        };
+
+        result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
+        if (result != noErr)
+            continue;
+
+        buflist = (AudioBufferList *) SDL_malloc(size);
+        if (buflist == NULL)
+            continue;
+
+        result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
+                                            &size, buflist);
+
+        if (result == noErr) {
+            UInt32 j;
+            for (j = 0; j < buflist->mNumberBuffers; j++) {
+                if (buflist->mBuffers[j].mNumberChannels > 0) {
+                    usable = 1;
+                    break;
+                }
+            }
+        }
+
+        SDL_free(buflist);
+
+        if (!usable)
+            continue;
+
+
+        size = sizeof (CFStringRef);
+        result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
+        if (result != kAudioHardwareNoError)
+            continue;
+
+        len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
+                                                kCFStringEncodingUTF8);
+
+        ptr = (char *) SDL_malloc(len + 1);
+        usable = ((ptr != NULL) &&
+                  (CFStringGetCString
+                   (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
+
+        CFRelease(cfstr);
+
+        if (usable) {
+            len = strlen(ptr);
+            /* Some devices have whitespace at the end...trim it. */
+            while ((len > 0) && (ptr[len - 1] == ' ')) {
+                len--;
+            }
+            usable = (len > 0);
+        }
+
+        if (usable) {
+            ptr[len] = '\0';
+
+#if DEBUG_COREAUDIO
+            printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
+                   ((iscapture) ? "capture" : "output"),
+                   (int) i, ptr, (int) dev);
+#endif
+            addfn(ptr, iscapture, dev, addfndata);
+        }
+        SDL_free(ptr);  /* addfn() would have copied the string. */
+    }
+}
+
+static void
+free_audio_device_list(AudioDeviceList **list)
+{
+    AudioDeviceList *item = *list;
+    while (item) {
+        AudioDeviceList *next = item->next;
+        SDL_free(item);
+        item = next;
+    }
+    *list = NULL;
+}
+
+static void
+COREAUDIO_DetectDevices(void)
+{
+    build_device_list(SDL_TRUE, addToDevList, NULL);
+    build_device_list(SDL_FALSE, addToDevList, NULL);
+}
+
+static void
+build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
+{
+    AudioDeviceList **list = (AudioDeviceList **) data;
+    AudioDeviceList *item;
+    for (item = *list; item != NULL; item = item->next) {
+        if (item->devid == devId) {
+            item->alive = SDL_TRUE;
+            return;
+        }
+    }
+
+    add_to_internal_dev_list(iscapture, devId);  /* new device, add it. */
+    SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
+}
+
+static void
+reprocess_device_list(const int iscapture, AudioDeviceList **list)
+{
+    AudioDeviceList *item;
+    AudioDeviceList *prev = NULL;
+    for (item = *list; item != NULL; item = item->next) {
+        item->alive = SDL_FALSE;
+    }
+
+    build_device_list(iscapture, build_device_change_list, list);
+
+    /* free items in the list that aren't still alive. */
+    item = *list;
+    while (item != NULL) {
+        AudioDeviceList *next = item->next;
+        if (item->alive) {
+            prev = item;
+        } else {
+            SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
+            if (prev) {
+                prev->next = item->next;
+            } else {
+                *list = item->next;
+            }
+            SDL_free(item);
+        }
+        item = next;
+    }
+}
+
+/* this is called when the system's list of available audio devices changes. */
+static OSStatus
+device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
+{
+    reprocess_device_list(SDL_TRUE, &capture_devs);
+    reprocess_device_list(SDL_FALSE, &output_devs);
+    return 0;
+}
+#endif
+
+
+static int open_playback_devices = 0;
+static int open_capture_devices = 0;
+
+#if !MACOSX_COREAUDIO
+
+static void interruption_begin(_THIS)
+{
+    if (this != NULL && this->hidden->audioQueue != NULL) {
+        this->hidden->interrupted = SDL_TRUE;
+        AudioQueuePause(this->hidden->audioQueue);
+    }
+}
+
+static void interruption_end(_THIS)
+{
+    if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
+    && this->hidden->interrupted) {
+        this->hidden->interrupted = SDL_FALSE;
+        AudioQueueStart(this->hidden->audioQueue, NULL);
+    }
+}
+
+@interface SDLInterruptionListener : NSObject
+
+@property (nonatomic, assign) SDL_AudioDevice *device;
+
+@end
+
+@implementation SDLInterruptionListener
+
+- (void)audioSessionInterruption:(NSNotification *)note
+{
+    @synchronized (self) {
+        NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
+        if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
+            interruption_begin(self.device);
+        } else {
+            interruption_end(self.device);
+        }
+    }
+}
+
+- (void)applicationBecameActive:(NSNotification *)note
+{
+    @synchronized (self) {
+        interruption_end(self.device);
+    }
+}
+
+@end
+
+static BOOL update_audio_session(_THIS, SDL_bool open)
+{
+    @autoreleasepool {
+        AVAudioSession *session = [AVAudioSession sharedInstance];
+        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+        NSString *category;
+        NSError *err = nil;
+
+        if (open_playback_devices && open_capture_devices) {
+            category = AVAudioSessionCategoryPlayAndRecord;
+        } else if (open_capture_devices) {
+            category = AVAudioSessionCategoryRecord;
+        } else {
+            /* Set category to ambient so that other music continues playing.
+             You can change this at runtime in your own code if you need different
+             behavior. If this is common, we can add an SDL hint for this. */
+            category = AVAudioSessionCategoryAmbient;
+        }
+
+        if (![session setCategory:category error:&err]) {
+            NSString *desc = err.description;
+            SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
+            return NO;
+        }
+
+        if (open_playback_devices + open_capture_devices == 1) {
+            if (![session setActive:YES error:&err]) {
+                NSString *desc = err.description;
+                SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
+                return NO;
+            }
+        } else if (!open_playback_devices && !open_capture_devices) {
+            [session setActive:NO error:nil];
+        }
+
+        if (open) {
+            SDLInterruptionListener *listener = [SDLInterruptionListener new];
+            listener.device = this;
+
+            [center addObserver:listener
+                       selector:@selector(audioSessionInterruption:)
+                           name:AVAudioSessionInterruptionNotification
+                         object:session];
+
+            /* An interruption end notification is not guaranteed to be sent if
+             we were previously interrupted... resuming if needed when the app
+             becomes active seems to be the way to go. */
+            [center addObserver:listener
+                       selector:@selector(applicationBecameActive:)
+                           name:UIApplicationDidBecomeActiveNotification
+                         object:session];
+
+            [center addObserver:listener
+                       selector:@selector(applicationBecameActive:)
+                           name:UIApplicationWillEnterForegroundNotification
+                         object:session];
+
+            this->hidden->interruption_listener = CFBridgingRetain(listener);
+        } else {
+            if (this->hidden->interruption_listener != NULL) {
+                SDLInterruptionListener *listener = nil;
+                listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
+                @synchronized (listener) {
+                    listener.device = NULL;
+                }
+                [center removeObserver:listener];
+            }
+        }
+    }
+
+    return YES;
+}
+#endif
+
+
+/* The AudioQueue callback */
+static void
+outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
+{
+    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
+    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
+        /* Supply silence if audio is enabled and not paused */
+        SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
+    } else {
+        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
+        Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
+
+        while (remaining > 0) {
+            UInt32 len;
+            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
+                /* Generate the data */
+                SDL_LockMutex(this->mixer_lock);
+                (*this->spec.callback)(this->spec.userdata,
+                            this->hidden->buffer, this->hidden->bufferSize);
+                SDL_UnlockMutex(this->mixer_lock);
+                this->hidden->bufferOffset = 0;
+            }
+
+            len = this->hidden->bufferSize - this->hidden->bufferOffset;
+            if (len > remaining) {
+                len = remaining;
+            }
+            SDL_memcpy(ptr, (char *)this->hidden->buffer +
+                       this->hidden->bufferOffset, len);
+            ptr = ptr + len;
+            remaining -= len;
+            this->hidden->bufferOffset += len;
+        }
+    }
+
+    if (!SDL_AtomicGet(&this->hidden->shutdown)) {
+        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+    }
+
+    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
+}
+
+static void
+inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
+              const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
+              const AudioStreamPacketDescription *inPacketDescs )
+{
+    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
+    if (SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {  /* ignore unless we're active. */
+        const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
+        UInt32 remaining = inBuffer->mAudioDataByteSize;
+        while (remaining > 0) {
+            UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
+            if (len > remaining) {
+                len = remaining;
+            }
+
+            SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
+            ptr += len;
+            remaining -= len;
+            this->hidden->bufferOffset += len;
+
+            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
+                SDL_LockMutex(this->mixer_lock);
+                (*this->spec.callback)(this->spec.userdata, this->hidden->buffer, this->hidden->bufferSize);
+                SDL_UnlockMutex(this->mixer_lock);
+                this->hidden->bufferOffset = 0;
+            }
+        }
+    }
+
+    if (!SDL_AtomicGet(&this->hidden->shutdown)) {
+        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+    }
+}
+
+
+#if MACOSX_COREAUDIO
+static const AudioObjectPropertyAddress alive_address =
+{
+    kAudioDevicePropertyDeviceIsAlive,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+};
+
+static OSStatus
+device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
+{
+    SDL_AudioDevice *this = (SDL_AudioDevice *) data;
+    SDL_bool dead = SDL_FALSE;
+    UInt32 isAlive = 1;
+    UInt32 size = sizeof (isAlive);
+    OSStatus error;
+
+    if (!SDL_AtomicGet(&this->enabled)) {
+        return 0;  /* already known to be dead. */
+    }
+
+    error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
+                                       0, NULL, &size, &isAlive);
+
+    if (error == kAudioHardwareBadDeviceError) {
+        dead = SDL_TRUE;  /* device was unplugged. */
+    } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
+        dead = SDL_TRUE;  /* device died in some other way. */
+    }
+
+    if (dead) {
+        SDL_OpenedAudioDeviceDisconnected(this);
+    }
+
+    return 0;
+}
+#endif
+
+static void
+COREAUDIO_CloseDevice(_THIS)
+{
+    const SDL_bool iscapture = this->iscapture;
+    int i;
+
+/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
+/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
+#if MACOSX_COREAUDIO
+    /* Fire a callback if the device stops being "alive" (disconnected, etc). */
+    AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
+#endif
+
+#if !MACOSX_COREAUDIO
+    update_audio_session(this, SDL_FALSE);
+#endif
+
+    if (this->hidden->thread) {
+        SDL_AtomicSet(&this->hidden->shutdown, 1);
+        SDL_WaitThread(this->hidden->thread, NULL);
+    }
+
+    if (this->hidden->audioQueue) {
+        for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
+            if (this->hidden->audioBuffer[i]) {
+                AudioQueueFreeBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i]);
+            }
+        }
+        AudioQueueDispose(this->hidden->audioQueue, 1);
+    }
+
+    if (this->hidden->ready_semaphore) {
+        SDL_DestroySemaphore(this->hidden->ready_semaphore);
+    }
+
+    SDL_free(this->hidden->thread_error);
+    SDL_free(this->hidden->buffer);
+    SDL_free(this->hidden);
+
+    if (iscapture) {
+        open_capture_devices--;
+    } else {
+        open_playback_devices--;
+    }
+}
+
+#if MACOSX_COREAUDIO
+static int
+prepare_device(_THIS, void *handle, int iscapture)
+{
+    AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
+    OSStatus result = noErr;
+    UInt32 size = 0;
+    UInt32 alive = 0;
+    pid_t pid = 0;
+
+    AudioObjectPropertyAddress addr = {
+        0,
+        kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster
+    };
+
+    if (handle == NULL) {
+        size = sizeof (AudioDeviceID);
+        addr.mSelector =
+            ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
+            kAudioHardwarePropertyDefaultOutputDevice);
+        result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
+                                            0, NULL, &size, &devid);
+        CHECK_RESULT("AudioHardwareGetProperty (default device)");
+    }
+
+    addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
+    addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
+                    kAudioDevicePropertyScopeOutput;
+
+    size = sizeof (alive);
+    result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
+    CHECK_RESULT
+        ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
+
+    if (!alive) {
+        SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
+        return 0;
+    }
+
+    addr.mSelector = kAudioDevicePropertyHogMode;
+    size = sizeof (pid);
+    result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
+
+    /* some devices don't support this property, so errors are fine here. */
+    if ((result == noErr) && (pid != -1)) {
+        SDL_SetError("CoreAudio: requested device is being hogged.");
+        return 0;
+    }
+
+    this->hidden->deviceID = devid;
+    return 1;
+}
+#endif
+
+static int
+prepare_audioqueue(_THIS)
+{
+    const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
+    const int iscapture = this->iscapture;
+    OSStatus result;
+    int i;
+
+    SDL_assert(CFRunLoopGetCurrent() != NULL);
+
+    if (iscapture) {
+        result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
+        CHECK_RESULT("AudioQueueNewInput");
+    } else {
+        result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
+        CHECK_RESULT("AudioQueueNewOutput");
+    }
+
+#if MACOSX_COREAUDIO
+{
+    const AudioObjectPropertyAddress prop = {
+        kAudioDevicePropertyDeviceUID,
+        iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
+        kAudioObjectPropertyElementMaster
+    };
+    CFStringRef devuid;
+    UInt32 devuidsize = sizeof (devuid);
+    result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
+    CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
+    result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
+    CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
+
+    /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
+    /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
+    /* Fire a callback if the device stops being "alive" (disconnected, etc). */
+    AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
+}
+#endif
+
+    /* Calculate the final parameters for this audio specification */
+    SDL_CalculateAudioSpec(&this->spec);
+
+    /* Allocate a sample buffer */
+    this->hidden->bufferSize = this->spec.size;
+    this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
+
+    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
+    if (this->hidden->buffer == NULL) {
+        SDL_OutOfMemory();
+        return 0;
+    }
+
+    for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
+        result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
+        CHECK_RESULT("AudioQueueAllocateBuffer");
+        SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
+        this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
+        result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
+        CHECK_RESULT("AudioQueueEnqueueBuffer");
+    }
+
+    result = AudioQueueStart(this->hidden->audioQueue, NULL);
+    CHECK_RESULT("AudioQueueStart");
+
+    /* We're running! */
+    return 1;
+}
+
+static int
+audioqueue_thread(void *arg)
+{
+    SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
+    const int rc = prepare_audioqueue(this);
+    if (!rc) {
+        this->hidden->thread_error = SDL_strdup(SDL_GetError());
+        SDL_SemPost(this->hidden->ready_semaphore);
+        return 0;
+    }
+
+    /* init was successful, alert parent thread and start running... */
+    SDL_SemPost(this->hidden->ready_semaphore);
+    while (!SDL_AtomicGet(&this->hidden->shutdown)) {
+        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
+    }
+
+    if (this->iscapture) {  /* just stop immediately for capture devices. */
+        AudioQueueStop(this->hidden->audioQueue, 1);
+    } else {  /* Drain off any pending playback. */
+        AudioQueueStop(this->hidden->audioQueue, 0);
+        const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
+        CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
+    }
+
+    return 0;
+}
+
+static int
+COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+{
+    AudioStreamBasicDescription *strdesc;
+    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
+    int valid_datatype = 0;
+
+    /* Initialize all variables that we clean on shutdown */
+    this->hidden = (struct SDL_PrivateAudioData *)
+        SDL_malloc((sizeof *this->hidden));
+    if (this->hidden == NULL) {
+        return SDL_OutOfMemory();
+    }
+    SDL_zerop(this->hidden);
+
+    strdesc = &this->hidden->strdesc;
+
+    if (iscapture) {
+        open_capture_devices++;
+    } else {
+        open_playback_devices++;
+    }
+
+#if !MACOSX_COREAUDIO
+    if (!update_audio_session(this, SDL_TRUE)) {
+        return -1;
+    }
+#endif
+
+    /* Setup a AudioStreamBasicDescription with the requested format */
+    SDL_zerop(strdesc);
+    strdesc->mFormatID = kAudioFormatLinearPCM;
+    strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
+    strdesc->mChannelsPerFrame = this->spec.channels;
+    strdesc->mSampleRate = this->spec.freq;
+    strdesc->mFramesPerPacket = 1;
+
+    while ((!valid_datatype) && (test_format)) {
+        this->spec.format = test_format;
+        /* Just a list of valid SDL formats, so people don't pass junk here. */
+        switch (test_format) {
+        case AUDIO_U8:
+        case AUDIO_S8:
+        case AUDIO_U16LSB:
+        case AUDIO_S16LSB:
+        case AUDIO_U16MSB:
+        case AUDIO_S16MSB:
+        case AUDIO_S32LSB:
+        case AUDIO_S32MSB:
+        case AUDIO_F32LSB:
+        case AUDIO_F32MSB:
+            valid_datatype = 1;
+            strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
+            if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
+                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+
+            if (SDL_AUDIO_ISFLOAT(this->spec.format))
+                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
+            else if (SDL_AUDIO_ISSIGNED(this->spec.format))
+                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+            break;
+        }
+    }
+
+    if (!valid_datatype) {      /* shouldn't happen, but just in case... */
+        return SDL_SetError("Unsupported audio format");
+    }
+
+    strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
+    strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
+
+#if MACOSX_COREAUDIO
+    if (!prepare_device(this, handle, iscapture)) {
+        return -1;
+    }
+#endif
+
+    /* This has to init in a new thread so it can get its own CFRunLoop. :/ */
+    SDL_AtomicSet(&this->hidden->shutdown, 0);
+    this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
+    if (!this->hidden->ready_semaphore) {
+        return -1;  /* oh well. */
+    }
+
+    this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
+    if (!this->hidden->thread) {
+        return -1;
+    }
+
+    SDL_SemWait(this->hidden->ready_semaphore);
+    SDL_DestroySemaphore(this->hidden->ready_semaphore);
+    this->hidden->ready_semaphore = NULL;
+
+    if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
+        SDL_SetError("%s", this->hidden->thread_error);
+        return -1;
+    }
+
+    return (this->hidden->thread != NULL) ? 0 : -1;
+}
+
+static void
+COREAUDIO_Deinitialize(void)
+{
+#if MACOSX_COREAUDIO
+    AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
+    free_audio_device_list(&capture_devs);
+    free_audio_device_list(&output_devs);
+#endif
+}
+
+static int
+COREAUDIO_Init(SDL_AudioDriverImpl * impl)
+{
+    /* Set the function pointers */
+    impl->OpenDevice = COREAUDIO_OpenDevice;
+    impl->CloseDevice = COREAUDIO_CloseDevice;
+    impl->Deinitialize = COREAUDIO_Deinitialize;
+
+#if MACOSX_COREAUDIO
+    impl->DetectDevices = COREAUDIO_DetectDevices;
+    AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
+#else
+    impl->OnlyHasDefaultOutputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
+#endif
+
+    impl->ProvidesOwnCallbackThread = 1;
+    impl->HasCaptureSupport = 1;
+
+    return 1;   /* this audio target is available. */
+}
+
+AudioBootStrap COREAUDIO_bootstrap = {
+    "coreaudio", "CoreAudio", COREAUDIO_Init, 0
+};
+
+#endif /* SDL_AUDIO_DRIVER_COREAUDIO */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 186 - 98
Engine/lib/sdl/src/audio/directsound/SDL_directsound.c

@@ -24,6 +24,7 @@
 
 /* Allow access to a raw mixing buffer */
 
+#include "SDL_assert.h"
 #include "SDL_timer.h"
 #include "SDL_loadso.h"
 #include "SDL_audio.h"
@@ -36,11 +37,13 @@
 
 /* DirectX function pointers for audio */
 static void* DSoundDLL = NULL;
-typedef HRESULT(WINAPI*fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
-typedef HRESULT(WINAPI*fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
-typedef HRESULT(WINAPI*fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
+typedef HRESULT (WINAPI *fnDirectSoundCreate8)(LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
+typedef HRESULT (WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+typedef HRESULT (WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID,LPDIRECTSOUNDCAPTURE8 *,LPUNKNOWN);
+typedef HRESULT (WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW,LPVOID);
 static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
 static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
+static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
 static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
 
 static void
@@ -48,6 +51,7 @@ DSOUND_Unload(void)
 {
     pDirectSoundCreate8 = NULL;
     pDirectSoundEnumerateW = NULL;
+    pDirectSoundCaptureCreate8 = NULL;
     pDirectSoundCaptureEnumerateW = NULL;
 
     if (DSoundDLL != NULL) {
@@ -76,6 +80,7 @@ DSOUND_Load(void)
         loaded = 1;  /* will reset if necessary. */
         DSOUNDLOAD(DirectSoundCreate8);
         DSOUNDLOAD(DirectSoundEnumerateW);
+        DSOUNDLOAD(DirectSoundCaptureCreate8);
         DSOUNDLOAD(DirectSoundCaptureEnumerateW);
         #undef DSOUNDLOAD
 
@@ -155,7 +160,7 @@ FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
 {
     const int iscapture = (int) ((size_t) data);
     if (guid != NULL) {  /* skip default device */
-        char *str = WIN_StringToUTF8(desc);
+        char *str = WIN_LookupAudioDeviceName(desc, guid);
         if (str != NULL) {
             LPGUID cpyguid = (LPGUID) SDL_malloc(sizeof (GUID));
             SDL_memcpy(cpyguid, guid, sizeof (GUID));
@@ -197,7 +202,7 @@ DSOUND_WaitDevice(_THIS)
         return;
     }
 
-    while ((cursor / this->hidden->mixlen) == this->hidden->lastchunk) {
+    while ((cursor / this->spec.size) == this->hidden->lastchunk) {
         /* FIXME: find out how much time is left and sleep that long */
         SDL_Delay(1);
 
@@ -239,9 +244,8 @@ DSOUND_PlayDevice(_THIS)
     if (this->hidden->locked_buf) {
         IDirectSoundBuffer_Unlock(this->hidden->mixbuf,
                                   this->hidden->locked_buf,
-                                  this->hidden->mixlen, NULL, 0);
+                                  this->spec.size, NULL, 0);
     }
-
 }
 
 static Uint8 *
@@ -265,7 +269,7 @@ DSOUND_GetDeviceBuf(_THIS)
         SetDSerror("DirectSound GetCurrentPosition", result);
         return (NULL);
     }
-    cursor /= this->hidden->mixlen;
+    cursor /= this->spec.size;
 #ifdef DEBUG_SOUND
     /* Detect audio dropouts */
     {
@@ -281,17 +285,17 @@ DSOUND_GetDeviceBuf(_THIS)
 #endif
     this->hidden->lastchunk = cursor;
     cursor = (cursor + 1) % this->hidden->num_buffers;
-    cursor *= this->hidden->mixlen;
+    cursor *= this->spec.size;
 
     /* Lock the audio buffer */
     result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
-                                     this->hidden->mixlen,
+                                     this->spec.size,
                                      (LPVOID *) & this->hidden->locked_buf,
                                      &rawlen, NULL, &junk, 0);
     if (result == DSERR_BUFFERLOST) {
         IDirectSoundBuffer_Restore(this->hidden->mixbuf);
         result = IDirectSoundBuffer_Lock(this->hidden->mixbuf, cursor,
-                                         this->hidden->mixlen,
+                                         this->spec.size,
                                          (LPVOID *) & this->
                                          hidden->locked_buf, &rawlen, NULL,
                                          &junk, 0);
@@ -303,109 +307,106 @@ DSOUND_GetDeviceBuf(_THIS)
     return (this->hidden->locked_buf);
 }
 
-static void
-DSOUND_WaitDone(_THIS)
+static int
+DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
 {
-    Uint8 *stream = DSOUND_GetDeviceBuf(this);
+    struct SDL_PrivateAudioData *h = this->hidden;
+    DWORD junk, cursor, ptr1len, ptr2len;
+    VOID *ptr1, *ptr2;
+
+    SDL_assert(buflen == this->spec.size);
+
+    while (SDL_TRUE) {
+        if (SDL_AtomicGet(&this->shutdown)) {  /* in case the buffer froze... */
+            SDL_memset(buffer, this->spec.silence, buflen);
+            return buflen;
+        }
+
+        if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
+            return -1;
+        }
+        if ((cursor / this->spec.size) == h->lastchunk) {
+            SDL_Delay(1);  /* FIXME: find out how much time is left and sleep that long */
+        } else {
+            break;
+        }
+    }
+
+    if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * this->spec.size, this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
+        return -1;
+    }
+
+    SDL_assert(ptr1len == this->spec.size);
+    SDL_assert(ptr2 == NULL);
+    SDL_assert(ptr2len == 0);
+
+    SDL_memcpy(buffer, ptr1, ptr1len);
 
-    /* Wait for the playing chunk to finish */
-    if (stream != NULL) {
-        SDL_memset(stream, this->spec.silence, this->hidden->mixlen);
-        DSOUND_PlayDevice(this);
+    if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
+        return -1;
     }
-    DSOUND_WaitDevice(this);
 
-    /* Stop the looping sound buffer */
-    IDirectSoundBuffer_Stop(this->hidden->mixbuf);
+    h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
+
+    return ptr1len;
 }
 
 static void
-DSOUND_CloseDevice(_THIS)
+DSOUND_FlushCapture(_THIS)
 {
-    if (this->hidden != NULL) {
-        if (this->hidden->sound != NULL) {
-            if (this->hidden->mixbuf != NULL) {
-                /* Clean up the audio buffer */
-                IDirectSoundBuffer_Release(this->hidden->mixbuf);
-                this->hidden->mixbuf = NULL;
-            }
-            IDirectSound_Release(this->hidden->sound);
-            this->hidden->sound = NULL;
-        }
+    struct SDL_PrivateAudioData *h = this->hidden;
+    DWORD junk, cursor;
+    if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
+        h->lastchunk = cursor / this->spec.size;
+    }
+}
 
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+static void
+DSOUND_CloseDevice(_THIS)
+{
+    if (this->hidden->mixbuf != NULL) {
+        IDirectSoundBuffer_Stop(this->hidden->mixbuf);
+        IDirectSoundBuffer_Release(this->hidden->mixbuf);
+    }
+    if (this->hidden->sound != NULL) {
+        IDirectSound_Release(this->hidden->sound);
     }
+    if (this->hidden->capturebuf != NULL) {
+        IDirectSoundCaptureBuffer_Stop(this->hidden->capturebuf);
+        IDirectSoundCaptureBuffer_Release(this->hidden->capturebuf);
+    }
+    if (this->hidden->capture != NULL) {
+        IDirectSoundCapture_Release(this->hidden->capture);
+    }
+    SDL_free(this->hidden);
 }
 
 /* This function tries to create a secondary audio buffer, and returns the
-   number of audio chunks available in the created buffer.
+   number of audio chunks available in the created buffer. This is for
+   playback devices, not capture.
 */
 static int
-CreateSecondary(_THIS, HWND focus)
+CreateSecondary(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
 {
     LPDIRECTSOUND sndObj = this->hidden->sound;
     LPDIRECTSOUNDBUFFER *sndbuf = &this->hidden->mixbuf;
-    Uint32 chunksize = this->spec.size;
-    const int numchunks = 8;
     HRESULT result = DS_OK;
     DSBUFFERDESC format;
     LPVOID pvAudioPtr1, pvAudioPtr2;
     DWORD dwAudioBytes1, dwAudioBytes2;
-    WAVEFORMATEX wfmt;
-
-    SDL_zero(wfmt);
-
-    if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
-        wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
-    } else {
-        wfmt.wFormatTag = WAVE_FORMAT_PCM;
-    }
-
-    wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
-    wfmt.nChannels = this->spec.channels;
-    wfmt.nSamplesPerSec = this->spec.freq;
-    wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
-    wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
-
-    /* Update the fragment size as size in bytes */
-    SDL_CalculateAudioSpec(&this->spec);
-
-    /* Try to set primary mixing privileges */
-    if (focus) {
-        result = IDirectSound_SetCooperativeLevel(sndObj,
-                                                  focus, DSSCL_PRIORITY);
-    } else {
-        result = IDirectSound_SetCooperativeLevel(sndObj,
-                                                  GetDesktopWindow(),
-                                                  DSSCL_NORMAL);
-    }
-    if (result != DS_OK) {
-        return SetDSerror("DirectSound SetCooperativeLevel", result);
-    }
 
     /* Try to create the secondary buffer */
     SDL_zero(format);
     format.dwSize = sizeof(format);
     format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
-    if (!focus) {
-        format.dwFlags |= DSBCAPS_GLOBALFOCUS;
-    } else {
-        format.dwFlags |= DSBCAPS_STICKYFOCUS;
-    }
-    format.dwBufferBytes = numchunks * chunksize;
-    if ((format.dwBufferBytes < DSBSIZE_MIN) ||
-        (format.dwBufferBytes > DSBSIZE_MAX)) {
-        return SDL_SetError("Sound buffer size must be between %d and %d",
-                            DSBSIZE_MIN / numchunks, DSBSIZE_MAX / numchunks);
-    }
-    format.dwReserved = 0;
-    format.lpwfxFormat = &wfmt;
+    format.dwFlags |= DSBCAPS_GLOBALFOCUS;
+    format.dwBufferBytes = bufsize;
+    format.lpwfxFormat = wfmt;
     result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
     if (result != DS_OK) {
         return SetDSerror("DirectSound CreateSoundBuffer", result);
     }
-    IDirectSoundBuffer_SetFormat(*sndbuf, &wfmt);
+    IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
 
     /* Silence the initial audio buffer */
     result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
@@ -420,31 +421,90 @@ CreateSecondary(_THIS, HWND focus)
     }
 
     /* We're ready to go */
-    return (numchunks);
+    return 0;
+}
+
+/* This function tries to create a capture buffer, and returns the
+   number of audio chunks available in the created buffer. This is for
+   capture devices, not playback.
+*/
+static int
+CreateCaptureBuffer(_THIS, const DWORD bufsize, WAVEFORMATEX *wfmt)
+{
+    LPDIRECTSOUNDCAPTURE capture = this->hidden->capture;
+    LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &this->hidden->capturebuf;
+    DSCBUFFERDESC format;
+//    DWORD junk, cursor;
+    HRESULT result;
+
+    SDL_zero(format);
+    format.dwSize = sizeof (format);
+    format.dwFlags = DSCBCAPS_WAVEMAPPED;
+    format.dwBufferBytes = bufsize;
+    format.lpwfxFormat = wfmt;
+
+    result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
+    if (result != DS_OK) {
+        return SetDSerror("DirectSound CreateCaptureBuffer", result);
+    }
+
+    result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
+    if (result != DS_OK) {
+        IDirectSoundCaptureBuffer_Release(*capturebuf);
+        return SetDSerror("DirectSound Start", result);
+    }
+
+#if 0
+    /* presumably this starts at zero, but just in case... */
+    result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
+    if (result != DS_OK) {
+        IDirectSoundCaptureBuffer_Stop(*capturebuf);
+        IDirectSoundCaptureBuffer_Release(*capturebuf);
+        return SetDSerror("DirectSound GetCurrentPosition", result);
+    }
+
+    this->hidden->lastchunk = cursor / this->spec.size;
+#endif
+
+    return 0;
 }
 
 static int
 DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
+    const DWORD numchunks = 8;
     HRESULT result;
     SDL_bool valid_format = SDL_FALSE;
     SDL_bool tried_format = SDL_FALSE;
     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
     LPGUID guid = (LPGUID) handle;
-
+	DWORD bufsize;
+	
     /* Initialize all variables that we clean on shutdown */
     this->hidden = (struct SDL_PrivateAudioData *)
         SDL_malloc((sizeof *this->hidden));
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Open the audio device */
-    result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
-    if (result != DS_OK) {
-        DSOUND_CloseDevice(this);
-        return SetDSerror("DirectSoundCreate", result);
+    if (iscapture) {
+        result = pDirectSoundCaptureCreate8(guid, &this->hidden->capture, NULL);
+        if (result != DS_OK) {
+            return SetDSerror("DirectSoundCaptureCreate8", result);
+        }
+    } else {
+        result = pDirectSoundCreate8(guid, &this->hidden->sound, NULL);
+        if (result != DS_OK) {
+            return SetDSerror("DirectSoundCreate8", result);
+        }
+        result = IDirectSound_SetCooperativeLevel(this->hidden->sound,
+                                                  GetDesktopWindow(),
+                                                  DSSCL_NORMAL);
+        if (result != DS_OK) {
+            return SetDSerror("DirectSound SetCooperativeLevel", result);
+        }
     }
 
     while ((!valid_format) && (test_format)) {
@@ -454,10 +514,38 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         case AUDIO_S32:
         case AUDIO_F32:
             tried_format = SDL_TRUE;
+
             this->spec.format = test_format;
-            this->hidden->num_buffers = CreateSecondary(this, NULL);
-            if (this->hidden->num_buffers > 0) {
-                valid_format = SDL_TRUE;
+
+            /* Update the fragment size as size in bytes */
+            SDL_CalculateAudioSpec(&this->spec);
+
+            bufsize = numchunks * this->spec.size;
+            if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
+                SDL_SetError("Sound buffer size must be between %d and %d",
+                             (DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks,
+                             DSBSIZE_MAX / numchunks);
+            } else {
+                int rc;
+				WAVEFORMATEX wfmt;
+                SDL_zero(wfmt);
+                if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
+                    wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+                } else {
+                    wfmt.wFormatTag = WAVE_FORMAT_PCM;
+                }
+
+                wfmt.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
+                wfmt.nChannels = this->spec.channels;
+                wfmt.nSamplesPerSec = this->spec.freq;
+                wfmt.nBlockAlign = wfmt.nChannels * (wfmt.wBitsPerSample / 8);
+                wfmt.nAvgBytesPerSec = wfmt.nSamplesPerSec * wfmt.nBlockAlign;
+
+                rc = iscapture ? CreateCaptureBuffer(this, bufsize, &wfmt) : CreateSecondary(this, bufsize, &wfmt);
+                if (rc == 0) {
+                    this->hidden->num_buffers = numchunks;
+                    valid_format = SDL_TRUE;
+                }
             }
             break;
         }
@@ -465,15 +553,13 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (!valid_format) {
-        DSOUND_CloseDevice(this);
         if (tried_format) {
             return -1;  /* CreateSecondary() should have called SDL_SetError(). */
         }
         return SDL_SetError("DirectSound: Unsupported audio format");
     }
 
-    /* The buffer will auto-start playing in DSOUND_WaitDevice() */
-    this->hidden->mixlen = this->spec.size;
+    /* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
 
     return 0;                   /* good to go. */
 }
@@ -498,13 +584,15 @@ DSOUND_Init(SDL_AudioDriverImpl * impl)
     impl->OpenDevice = DSOUND_OpenDevice;
     impl->PlayDevice = DSOUND_PlayDevice;
     impl->WaitDevice = DSOUND_WaitDevice;
-    impl->WaitDone = DSOUND_WaitDone;
     impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
+    impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
+    impl->FlushCapture = DSOUND_FlushCapture;
     impl->CloseDevice = DSOUND_CloseDevice;
     impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
-
     impl->Deinitialize = DSOUND_Deinitialize;
 
+    impl->HasCaptureSupport = SDL_TRUE;
+
     return 1;   /* this audio target is available. */
 }
 

+ 2 - 1
Engine/lib/sdl/src/audio/directsound/SDL_directsound.h

@@ -35,8 +35,9 @@ struct SDL_PrivateAudioData
 {
     LPDIRECTSOUND sound;
     LPDIRECTSOUNDBUFFER mixbuf;
+    LPDIRECTSOUNDCAPTURE capture;
+    LPDIRECTSOUNDCAPTUREBUFFER capturebuf;
     int num_buffers;
-    int mixlen;
     DWORD lastchunk;
     Uint8 *locked_buf;
 };

+ 96 - 63
Engine/lib/sdl/src/audio/disk/SDL_diskaudio.c

@@ -31,46 +31,33 @@
 #include "SDL_rwops.h"
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_diskaudio.h"
 
+/* !!! FIXME: these should be SDL hints, not environment variables. */
 /* environment variables and defaults. */
 #define DISKENVR_OUTFILE         "SDL_DISKAUDIOFILE"
 #define DISKDEFAULT_OUTFILE      "sdlaudio.raw"
-#define DISKENVR_WRITEDELAY      "SDL_DISKAUDIODELAY"
-#define DISKDEFAULT_WRITEDELAY   150
-
-static const char *
-DISKAUD_GetOutputFilename(const char *devname)
-{
-    if (devname == NULL) {
-        devname = SDL_getenv(DISKENVR_OUTFILE);
-        if (devname == NULL) {
-            devname = DISKDEFAULT_OUTFILE;
-        }
-    }
-    return devname;
-}
+#define DISKENVR_INFILE         "SDL_DISKAUDIOFILEIN"
+#define DISKDEFAULT_INFILE      "sdlaudio-in.raw"
+#define DISKENVR_IODELAY      "SDL_DISKAUDIODELAY"
 
 /* This function waits until it is possible to write a full sound buffer */
 static void
-DISKAUD_WaitDevice(_THIS)
+DISKAUDIO_WaitDevice(_THIS)
 {
-    SDL_Delay(this->hidden->write_delay);
+    SDL_Delay(this->hidden->io_delay);
 }
 
 static void
-DISKAUD_PlayDevice(_THIS)
+DISKAUDIO_PlayDevice(_THIS)
 {
-    size_t written;
-
-    /* Write the audio data */
-    written = SDL_RWwrite(this->hidden->output,
-                          this->hidden->mixbuf, 1, this->hidden->mixlen);
+    const size_t written = SDL_RWwrite(this->hidden->io,
+                                       this->hidden->mixbuf,
+                                       1, this->spec.size);
 
     /* If we couldn't write, assume fatal error for now */
-    if (written != this->hidden->mixlen) {
+    if (written != this->spec.size) {
         SDL_OpenedAudioDeviceDisconnected(this);
     }
 #ifdef DEBUG_AUDIO
@@ -79,63 +66,105 @@ DISKAUD_PlayDevice(_THIS)
 }
 
 static Uint8 *
-DISKAUD_GetDeviceBuf(_THIS)
+DISKAUDIO_GetDeviceBuf(_THIS)
 {
     return (this->hidden->mixbuf);
 }
 
+static int
+DISKAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
+    const int origbuflen = buflen;
+
+    SDL_Delay(h->io_delay);
+
+    if (h->io) {
+        const size_t br = SDL_RWread(h->io, buffer, 1, buflen);
+        buflen -= (int) br;
+        buffer = ((Uint8 *) buffer) + br;
+        if (buflen > 0) {  /* EOF (or error, but whatever). */
+            SDL_RWclose(h->io);
+            h->io = NULL;
+        }
+    }
+
+    /* if we ran out of file, just write silence. */
+    SDL_memset(buffer, this->spec.silence, buflen);
+
+    return origbuflen;
+}
+
 static void
-DISKAUD_CloseDevice(_THIS)
+DISKAUDIO_FlushCapture(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->output != NULL) {
-            SDL_RWclose(this->hidden->output);
-            this->hidden->output = NULL;
+    /* no op...we don't advance the file pointer or anything. */
+}
+
+
+static void
+DISKAUDIO_CloseDevice(_THIS)
+{
+    if (this->hidden->io != NULL) {
+        SDL_RWclose(this->hidden->io);
+    }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
+}
+
+
+static const char *
+get_filename(const int iscapture, const char *devname)
+{
+    if (devname == NULL) {
+        devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
+        if (devname == NULL) {
+            devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE;
         }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
     }
+    return devname;
 }
 
 static int
-DISKAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+DISKAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
     /* handle != NULL means "user specified the placeholder name on the fake detected device list" */
-    const char *fname = DISKAUD_GetOutputFilename(handle ? NULL : devname);
-    const char *envr = SDL_getenv(DISKENVR_WRITEDELAY);
+    const char *fname = get_filename(iscapture, handle ? NULL : devname);
+    const char *envr = SDL_getenv(DISKENVR_IODELAY);
 
     this->hidden = (struct SDL_PrivateAudioData *)
         SDL_malloc(sizeof(*this->hidden));
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, sizeof(*this->hidden));
+    SDL_zerop(this->hidden);
 
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->write_delay =
-        (envr) ? SDL_atoi(envr) : DISKDEFAULT_WRITEDELAY;
+    if (envr != NULL) {
+        this->hidden->io_delay = SDL_atoi(envr);
+    } else {
+        this->hidden->io_delay = ((this->spec.samples * 1000) / this->spec.freq);
+    }
 
     /* Open the audio device */
-    this->hidden->output = SDL_RWFromFile(fname, "wb");
-    if (this->hidden->output == NULL) {
-        DISKAUD_CloseDevice(this);
+    this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
+    if (this->hidden->io == NULL) {
         return -1;
     }
 
     /* Allocate mixing buffer */
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        DISKAUD_CloseDevice(this);
-        return -1;
+    if (!iscapture) {
+        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
+        if (this->hidden->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
 #if HAVE_STDIO_H
     fprintf(stderr,
-            "WARNING: You are using the SDL disk writer audio driver!\n"
-            " Writing to file [%s].\n", fname);
+            "WARNING: You are using the SDL disk i/o audio driver!\n"
+            " %s file [%s].\n", iscapture ? "Reading from" : "Writing to",
+            fname);
 #endif
 
     /* We're ready to rock and roll. :-) */
@@ -143,30 +172,34 @@ DISKAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 }
 
 static void
-DISKAUD_DetectDevices(void)
+DISKAUDIO_DetectDevices(void)
 {
-    /* !!! FIXME: stole this literal string from DEFAULT_OUTPUT_DEVNAME in SDL_audio.c */
-    SDL_AddAudioDevice(SDL_FALSE, "System audio output device", (void *) 0x1);
+    SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) 0x1);
+    SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) 0x2);
 }
 
 static int
-DISKAUD_Init(SDL_AudioDriverImpl * impl)
+DISKAUDIO_Init(SDL_AudioDriverImpl * impl)
 {
     /* Set the function pointers */
-    impl->OpenDevice = DISKAUD_OpenDevice;
-    impl->WaitDevice = DISKAUD_WaitDevice;
-    impl->PlayDevice = DISKAUD_PlayDevice;
-    impl->GetDeviceBuf = DISKAUD_GetDeviceBuf;
-    impl->CloseDevice = DISKAUD_CloseDevice;
-    impl->DetectDevices = DISKAUD_DetectDevices;
+    impl->OpenDevice = DISKAUDIO_OpenDevice;
+    impl->WaitDevice = DISKAUDIO_WaitDevice;
+    impl->PlayDevice = DISKAUDIO_PlayDevice;
+    impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
+    impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;
+    impl->FlushCapture = DISKAUDIO_FlushCapture;
+
+    impl->CloseDevice = DISKAUDIO_CloseDevice;
+    impl->DetectDevices = DISKAUDIO_DetectDevices;
 
     impl->AllowsArbitraryDeviceNames = 1;
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }
 
-AudioBootStrap DISKAUD_bootstrap = {
-    "disk", "direct-to-disk audio", DISKAUD_Init, 1
+AudioBootStrap DISKAUDIO_bootstrap = {
+    "disk", "direct-to-disk audio", DISKAUDIO_Init, 1
 };
 
 #endif /* SDL_AUDIO_DRIVER_DISK */

+ 2 - 3
Engine/lib/sdl/src/audio/disk/SDL_diskaudio.h

@@ -32,10 +32,9 @@
 struct SDL_PrivateAudioData
 {
     /* The file descriptor for the audio device */
-    SDL_RWops *output;
+    SDL_RWops *io;
+    Uint32 io_delay;
     Uint8 *mixbuf;
-    Uint32 mixlen;
-    Uint32 write_delay;
 };
 
 #endif /* _SDL_diskaudio_h */

+ 43 - 31
Engine/lib/sdl/src/audio/dsp/SDL_dspaudio.c

@@ -44,7 +44,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audiodev_c.h"
 #include "SDL_dspaudio.h"
@@ -60,16 +59,11 @@ DSP_DetectDevices(void)
 static void
 DSP_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->audio_fd >= 0) {
-            close(this->hidden->audio_fd);
-            this->hidden->audio_fd = -1;
-        }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->audio_fd >= 0) {
+        close(this->hidden->audio_fd);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 
@@ -106,23 +100,20 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Open the audio device */
     this->hidden->audio_fd = open(devname, flags, 0);
     if (this->hidden->audio_fd < 0) {
-        DSP_CloseDevice(this);
         return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
     }
-    this->hidden->mixbuf = NULL;
 
-    /* Make the file descriptor use blocking writes with fcntl() */
+    /* Make the file descriptor use blocking i/o with fcntl() */
     {
         long ctlflags;
         ctlflags = fcntl(this->hidden->audio_fd, F_GETFL);
         ctlflags &= ~O_NONBLOCK;
         if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
-            DSP_CloseDevice(this);
             return SDL_SetError("Couldn't set audio blocking mode");
         }
     }
@@ -130,7 +121,6 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Get a list of supported hardware formats */
     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
         perror("SNDCTL_DSP_GETFMTS");
-        DSP_CloseDevice(this);
         return SDL_SetError("Couldn't get audio format list");
     }
 
@@ -187,7 +177,6 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         }
     }
     if (format == 0) {
-        DSP_CloseDevice(this);
         return SDL_SetError("Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
@@ -197,7 +186,6 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if ((ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
         (value != format)) {
         perror("SNDCTL_DSP_SETFMT");
-        DSP_CloseDevice(this);
         return SDL_SetError("Couldn't set audio format");
     }
 
@@ -205,7 +193,6 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     value = this->spec.channels;
     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
         perror("SNDCTL_DSP_CHANNELS");
-        DSP_CloseDevice(this);
         return SDL_SetError("Cannot set the number of channels");
     }
     this->spec.channels = value;
@@ -214,7 +201,6 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     value = this->spec.freq;
     if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
         perror("SNDCTL_DSP_SPEED");
-        DSP_CloseDevice(this);
         return SDL_SetError("Couldn't set audio frequency");
     }
     this->spec.freq = value;
@@ -225,7 +211,6 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Determine the power of two of the fragment size */
     for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
     if ((0x01U << frag_spec) != this->spec.size) {
-        DSP_CloseDevice(this);
         return SDL_SetError("Fragment size must be a power of two");
     }
     frag_spec |= 0x00020000;    /* two fragments, for low latency */
@@ -250,13 +235,14 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 #endif
 
     /* Allocate mixing buffer */
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        DSP_CloseDevice(this);
-        return SDL_OutOfMemory();
+    if (!iscapture) {
+        this->hidden->mixlen = this->spec.size;
+        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
+        if (this->hidden->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
     /* We're ready to rock and roll. :-) */
     return 0;
@@ -266,14 +252,13 @@ DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 static void
 DSP_PlayDevice(_THIS)
 {
-    const Uint8 *mixbuf = this->hidden->mixbuf;
-    const int mixlen = this->hidden->mixlen;
-    if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
+    struct SDL_PrivateAudioData *h = this->hidden;
+    if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) {
         perror("Audio write");
         SDL_OpenedAudioDeviceDisconnected(this);
     }
 #ifdef DEBUG_AUDIO
-    fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
+    fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
 #endif
 }
 
@@ -283,6 +268,30 @@ DSP_GetDeviceBuf(_THIS)
     return (this->hidden->mixbuf);
 }
 
+static int
+DSP_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    return (int) read(this->hidden->audio_fd, buffer, buflen);
+}
+
+static void
+DSP_FlushCapture(_THIS)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
+    audio_buf_info info;
+    if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
+        while (info.bytes > 0) {
+            char buf[512];
+            const size_t len = SDL_min(sizeof (buf), info.bytes);
+            const ssize_t br = read(h->audio_fd, buf, len);
+            if (br <= 0) {
+                break;
+            }
+            info.bytes -= br;
+        }
+    }
+}
+
 static int
 DSP_Init(SDL_AudioDriverImpl * impl)
 {
@@ -292,8 +301,11 @@ DSP_Init(SDL_AudioDriverImpl * impl)
     impl->PlayDevice = DSP_PlayDevice;
     impl->GetDeviceBuf = DSP_GetDeviceBuf;
     impl->CloseDevice = DSP_CloseDevice;
+    impl->CaptureFromDevice = DSP_CaptureFromDevice;
+    impl->FlushCapture = DSP_FlushCapture;
 
     impl->AllowsArbitraryDeviceNames = 1;
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }

+ 22 - 5
Engine/lib/sdl/src/audio/dummy/SDL_dummyaudio.c

@@ -22,27 +22,44 @@
 
 /* Output audio to nowhere... */
 
+#include "SDL_timer.h"
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
 #include "SDL_dummyaudio.h"
 
 static int
-DUMMYAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+DUMMYAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
     return 0;                   /* always succeeds. */
 }
 
 static int
-DUMMYAUD_Init(SDL_AudioDriverImpl * impl)
+DUMMYAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    /* Delay to make this sort of simulate real audio input. */
+    SDL_Delay((this->spec.samples * 1000) / this->spec.freq);
+
+    /* always return a full buffer of silence. */
+    SDL_memset(buffer, this->spec.silence, buflen);
+    return buflen;
+}
+
+static int
+DUMMYAUDIO_Init(SDL_AudioDriverImpl * impl)
 {
     /* Set the function pointers */
-    impl->OpenDevice = DUMMYAUD_OpenDevice;
+    impl->OpenDevice = DUMMYAUDIO_OpenDevice;
+    impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice;
+
     impl->OnlyHasDefaultOutputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
+    impl->HasCaptureSupport = SDL_TRUE;
+
     return 1;   /* this audio target is available. */
 }
 
-AudioBootStrap DUMMYAUD_bootstrap = {
-    "dummy", "SDL dummy audio driver", DUMMYAUD_Init, 1
+AudioBootStrap DUMMYAUDIO_bootstrap = {
+    "dummy", "SDL dummy audio driver", DUMMYAUDIO_Init, 1
 };
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 225 - 61
Engine/lib/sdl/src/audio/emscripten/SDL_emscriptenaudio.c

@@ -61,16 +61,15 @@ HandleAudioProcess(_THIS)
     Uint8 *buf = NULL;
     int byte_len = 0;
     int bytes = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
-    int bytes_in = SDL_AUDIO_BITSIZE(this->convert.src_format) / 8;
 
-    /* Only do soemthing if audio is enabled */
-    if (!this->enabled)
-        return;
-
-    if (this->paused)
+    /* Only do something if audio is enabled */
+    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
         return;
+    }
 
     if (this->convert.needed) {
+        const int bytes_in = SDL_AUDIO_BITSIZE(this->convert.src_format) / 8;
+
         if (this->hidden->conv_in_len != 0) {
             this->convert.len = this->hidden->conv_in_len * bytes_in * this->spec.channels;
         }
@@ -128,7 +127,7 @@ HandleAudioProcess(_THIS)
                 }
 
                 for (var j = 0; j < $1; ++j) {
-                    channelData[j] = getValue($0 + (j*numChannels + c)*4, 'float');
+                    channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2];
                 }
             }
         }, buf, byte_len / bytes / this->spec.channels);
@@ -136,29 +135,147 @@ HandleAudioProcess(_THIS)
 }
 
 static void
-Emscripten_CloseDevice(_THIS)
+HandleCaptureProcess(_THIS)
 {
-    if (this->hidden != NULL) {
-        if (this->hidden->mixbuf != NULL) {
-            /* Clean up the audio buffer */
-            SDL_free(this->hidden->mixbuf);
-            this->hidden->mixbuf = NULL;
+    Uint8 *buf;
+    int buflen;
+
+    /* Only do something if audio is enabled */
+    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
+        return;
+    }
+
+    if (this->convert.needed) {
+        buf = this->convert.buf;
+        buflen = this->convert.len_cvt;
+    } else {
+        if (!this->hidden->mixbuf) {
+            this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
+            if (!this->hidden->mixbuf) {
+                return;  /* oh well. */
+            }
+        }
+        buf = this->hidden->mixbuf;
+        buflen = this->spec.size;
+    }
+
+    EM_ASM_ARGS({
+        var numChannels = SDL2.capture.currentCaptureBuffer.numberOfChannels;
+        if (numChannels == 1) {  /* fastpath this a little for the common (mono) case. */
+            var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(0);
+            if (channelData.length != $1) {
+                throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
+            }
+            for (var j = 0; j < $1; ++j) {
+                setValue($0 + (j * 4), channelData[j], 'float');
+            }
+        } else {
+            for (var c = 0; c < numChannels; ++c) {
+                var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c);
+                if (channelData.length != $1) {
+                    throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
+                }
+
+                for (var j = 0; j < $1; ++j) {
+                    setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float');
+                }
+            }
         }
+    }, buf, (this->spec.size / sizeof (float)) / this->spec.channels);
+
+    /* okay, we've got an interleaved float32 array in C now. */
 
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->convert.needed) {
+        SDL_ConvertAudio(&this->convert);
     }
+
+    /* Send it to the app. */
+    (*this->spec.callback) (this->spec.userdata, buf, buflen);
+}
+
+
+
+static void
+EMSCRIPTENAUDIO_CloseDevice(_THIS)
+{
+    EM_ASM_({
+        if ($0) {
+            if (SDL2.capture.silenceTimer !== undefined) {
+                clearTimeout(SDL2.capture.silenceTimer);
+            }
+            if (SDL2.capture.stream !== undefined) {
+                var tracks = SDL2.capture.stream.getAudioTracks();
+                for (var i = 0; i < tracks.length; i++) {
+                    SDL2.capture.stream.removeTrack(tracks[i]);
+                }
+                SDL2.capture.stream = undefined;
+            }
+            if (SDL2.capture.scriptProcessorNode !== undefined) {
+                SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {};
+                SDL2.capture.scriptProcessorNode.disconnect();
+                SDL2.capture.scriptProcessorNode = undefined;
+            }
+            if (SDL2.capture.mediaStreamNode !== undefined) {
+                SDL2.capture.mediaStreamNode.disconnect();
+                SDL2.capture.mediaStreamNode = undefined;
+            }
+            if (SDL2.capture.silenceBuffer !== undefined) {
+                SDL2.capture.silenceBuffer = undefined
+            }
+            SDL2.capture = undefined;
+        } else {
+            if (SDL2.audio.scriptProcessorNode != undefined) {
+                SDL2.audio.scriptProcessorNode.disconnect();
+                SDL2.audio.scriptProcessorNode = undefined;
+            }
+            SDL2.audio = undefined;
+        }
+        if ((SDL2.audioContext !== undefined) && (SDL2.audio === undefined) && (SDL2.capture === undefined)) {
+            SDL2.audioContext.close();
+            SDL2.audioContext = undefined;
+        }
+    }, this->iscapture);
+
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
-Emscripten_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+EMSCRIPTENAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
     SDL_bool valid_format = SDL_FALSE;
-    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
+    SDL_AudioFormat test_format;
     int i;
     float f;
     int result;
 
+    /* based on parts of library_sdl.js */
+
+    /* create context (TODO: this puts stuff in the global namespace...)*/
+    result = EM_ASM_INT({
+        if(typeof(SDL2) === 'undefined') {
+            SDL2 = {};
+        }
+        if (!$0) {
+            SDL2.audio = {};
+        } else {
+            SDL2.capture = {};
+        }
+
+        if (!SDL2.audioContext) {
+            if (typeof(AudioContext) !== 'undefined') {
+                SDL2.audioContext = new AudioContext();
+            } else if (typeof(webkitAudioContext) !== 'undefined') {
+                SDL2.audioContext = new webkitAudioContext();
+            }
+        }
+        return SDL2.audioContext === undefined ? -1 : 0;
+    }, iscapture);
+    if (result < 0) {
+        return SDL_SetError("Web Audio API is not available!");
+    }
+
+    test_format = SDL_FirstAudioFormat(this->spec.format);
     while ((!valid_format) && (test_format)) {
         switch (test_format) {
         case AUDIO_F32: /* web audio only supports floats */
@@ -181,36 +298,11 @@ Emscripten_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
-
-    /* based on parts of library_sdl.js */
-
-    /* create context (TODO: this puts stuff in the global namespace...)*/
-    result = EM_ASM_INT_V({
-        if(typeof(SDL2) === 'undefined')
-            SDL2 = {};
-
-        if(typeof(SDL2.audio) === 'undefined')
-            SDL2.audio = {};
-
-        if (!SDL2.audioContext) {
-            if (typeof(AudioContext) !== 'undefined') {
-                SDL2.audioContext = new AudioContext();
-            } else if (typeof(webkitAudioContext) !== 'undefined') {
-                SDL2.audioContext = new webkitAudioContext();
-            } else {
-                return -1;
-            }
-        }
-        return 0;
-    });
-    if (result < 0) {
-        return SDL_SetError("Web Audio API is not available!");
-    }
+    SDL_zerop(this->hidden);
 
     /* limit to native freq */
-    int sampleRate = EM_ASM_INT_V({
-        return SDL2.audioContext['sampleRate'];
+    const int sampleRate = EM_ASM_INT_V({
+        return SDL2.audioContext.sampleRate;
     });
 
     if(this->spec.freq != sampleRate) {
@@ -227,26 +319,86 @@ Emscripten_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     SDL_CalculateAudioSpec(&this->spec);
 
-    /* setup a ScriptProcessorNode */
-    EM_ASM_ARGS({
-        SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
-        SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
-            SDL2.audio.currentOutputBuffer = e['outputBuffer'];
-            Runtime.dynCall('vi', $2, [$3]);
-        };
-        SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
-    }, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
+    if (iscapture) {
+        /* The idea is to take the capture media stream, hook it up to an
+           audio graph where we can pass it through a ScriptProcessorNode
+           to access the raw PCM samples and push them to the SDL app's
+           callback. From there, we "process" the audio data into silence
+           and forget about it. */
+
+        /* This should, strictly speaking, use MediaRecorder for capture, but
+           this API is cleaner to use and better supported, and fires a
+           callback whenever there's enough data to fire down into the app.
+           The downside is that we are spending CPU time silencing a buffer
+           that the audiocontext uselessly mixes into any output. On the
+           upside, both of those things are not only run in native code in
+           the browser, they're probably SIMD code, too. MediaRecorder
+           feels like it's a pretty inefficient tapdance in similar ways,
+           to be honest. */
+
+        EM_ASM_({
+            var have_microphone = function(stream) {
+                //console.log('SDL audio capture: we have a microphone! Replacing silence callback.');
+                if (SDL2.capture.silenceTimer !== undefined) {
+                    clearTimeout(SDL2.capture.silenceTimer);
+                    SDL2.capture.silenceTimer = undefined;
+                }
+                SDL2.capture.mediaStreamNode = SDL2.audioContext.createMediaStreamSource(stream);
+                SDL2.capture.scriptProcessorNode = SDL2.audioContext.createScriptProcessor($1, $0, 1);
+                SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {
+                    if ((SDL2 === undefined) || (SDL2.capture === undefined)) { return; }
+                    audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0);
+                    SDL2.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer;
+                    Runtime.dynCall('vi', $2, [$3]);
+                };
+                SDL2.capture.mediaStreamNode.connect(SDL2.capture.scriptProcessorNode);
+                SDL2.capture.scriptProcessorNode.connect(SDL2.audioContext.destination);
+                SDL2.capture.stream = stream;
+            };
+
+            var no_microphone = function(error) {
+                //console.log('SDL audio capture: we DO NOT have a microphone! (' + error.name + ')...leaving silence callback running.');
+            };
+
+            /* we write silence to the audio callback until the microphone is available (user approves use, etc). */
+            SDL2.capture.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate);
+            SDL2.capture.silenceBuffer.getChannelData(0).fill(0.0);
+            var silence_callback = function() {
+                SDL2.capture.currentCaptureBuffer = SDL2.capture.silenceBuffer;
+                Runtime.dynCall('vi', $2, [$3]);
+            };
+
+            SDL2.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000);
+
+            if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) {
+                navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone);
+            } else if (navigator.webkitGetUserMedia !== undefined) {
+                navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone);
+            }
+        }, this->spec.channels, this->spec.samples, HandleCaptureProcess, this);
+    } else {
+        /* setup a ScriptProcessorNode */
+        EM_ASM_ARGS({
+            SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
+            SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
+                if ((SDL2 === undefined) || (SDL2.audio === undefined)) { return; }
+                SDL2.audio.currentOutputBuffer = e['outputBuffer'];
+                Runtime.dynCall('vi', $2, [$3]);
+            };
+            SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
+        }, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
+    }
+
     return 0;
 }
 
 static int
-Emscripten_Init(SDL_AudioDriverImpl * impl)
+EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl * impl)
 {
     /* Set the function pointers */
-    impl->OpenDevice = Emscripten_OpenDevice;
-    impl->CloseDevice = Emscripten_CloseDevice;
+    impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
+    impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
 
-    /* only one output */
     impl->OnlyHasDefaultOutputDevice = 1;
 
     /* no threads here */
@@ -254,7 +406,7 @@ Emscripten_Init(SDL_AudioDriverImpl * impl)
     impl->ProvidesOwnCallbackThread = 1;
 
     /* check availability */
-    int available = EM_ASM_INT_V({
+    const int available = EM_ASM_INT_V({
         if (typeof(AudioContext) !== 'undefined') {
             return 1;
         } else if (typeof(webkitAudioContext) !== 'undefined') {
@@ -267,11 +419,23 @@ Emscripten_Init(SDL_AudioDriverImpl * impl)
         SDL_SetError("No audio context available");
     }
 
+    const int capture_available = available && EM_ASM_INT_V({
+        if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
+            return 1;
+        } else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') {
+            return 1;
+        }
+        return 0;
+    });
+
+    impl->HasCaptureSupport = capture_available ? SDL_TRUE : SDL_FALSE;
+    impl->OnlyHasDefaultCaptureDevice = capture_available ? SDL_TRUE : SDL_FALSE;
+
     return available;
 }
 
-AudioBootStrap EmscriptenAudio_bootstrap = {
-    "emscripten", "SDL emscripten audio driver", Emscripten_Init, 0
+AudioBootStrap EMSCRIPTENAUDIO_bootstrap = {
+    "emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, 0
 };
 
 #endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */

+ 6 - 16
Engine/lib/sdl/src/audio/esd/SDL_esdaudio.c

@@ -32,7 +32,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_esdaudio.h"
 
@@ -174,17 +173,11 @@ ESD_GetDeviceBuf(_THIS)
 static void
 ESD_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->audio_fd >= 0) {
-            SDL_NAME(esd_close) (this->hidden->audio_fd);
-            this->hidden->audio_fd = -1;
-        }
-
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->audio_fd >= 0) {
+        SDL_NAME(esd_close) (this->hidden->audio_fd);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 /* Try to get the name of the program */
@@ -227,7 +220,7 @@ ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
     this->hidden->audio_fd = -1;
 
     /* Convert audio spec to the ESD audio format */
@@ -252,7 +245,6 @@ ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (!found) {
-        ESD_CloseDevice(this);
         return SDL_SetError("Couldn't find any hardware audio formats");
     }
 
@@ -271,7 +263,6 @@ ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
                                    get_progname());
 
     if (this->hidden->audio_fd < 0) {
-        ESD_CloseDevice(this);
         return SDL_SetError("Couldn't open ESD connection");
     }
 
@@ -283,9 +274,8 @@ ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     /* Allocate mixing buffer */
     this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
+    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        ESD_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);

+ 11 - 28
Engine/lib/sdl/src/audio/fusionsound/SDL_fsaudio.c

@@ -22,6 +22,8 @@
 
 #if SDL_AUDIO_DRIVER_FUSIONSOUND
 
+/* !!! FIXME: why is this is SDL_FS_* instead of FUSIONSOUND_*? */
+
 /* Allow access to a raw mixing buffer */
 
 #ifdef HAVE_SIGNAL_H
@@ -31,7 +33,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_fsaudio.h"
 
@@ -150,13 +151,6 @@ SDL_FS_PlayDevice(_THIS)
 #endif
 }
 
-static void
-SDL_FS_WaitDone(_THIS)
-{
-    this->hidden->stream->Wait(this->hidden->stream,
-                               this->hidden->mixsamples * FUSION_BUFFERS);
-}
-
 
 static Uint8 *
 SDL_FS_GetDeviceBuf(_THIS)
@@ -168,20 +162,14 @@ SDL_FS_GetDeviceBuf(_THIS)
 static void
 SDL_FS_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->stream) {
-            this->hidden->stream->Release(this->hidden->stream);
-            this->hidden->stream = NULL;
-        }
-        if (this->hidden->fs) {
-            this->hidden->fs->Release(this->hidden->fs);
-            this->hidden->fs = NULL;
-        }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->stream) {
+        this->hidden->stream->Release(this->hidden->stream);
+    }
+    if (this->hidden->fs) {
+        this->hidden->fs->Release(this->hidden->fs);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 
@@ -200,7 +188,7 @@ SDL_FS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Try for a closest match on audio format */
     for (test_format = SDL_FirstAudioFormat(this->spec.format);
@@ -239,7 +227,6 @@ SDL_FS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (format == 0) {
-        SDL_FS_CloseDevice(this);
         return SDL_SetError("Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
@@ -247,7 +234,6 @@ SDL_FS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Retrieve the main sound interface. */
     ret = SDL_NAME(FusionSoundCreate) (&this->hidden->fs);
     if (ret) {
-        SDL_FS_CloseDevice(this);
         return SDL_SetError("Unable to initialize FusionSound: %d", ret);
     }
 
@@ -266,7 +252,6 @@ SDL_FS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         this->hidden->fs->CreateStream(this->hidden->fs, &desc,
                                        &this->hidden->stream);
     if (ret) {
-        SDL_FS_CloseDevice(this);
         return SDL_SetError("Unable to create FusionSoundStream: %d", ret);
     }
 
@@ -285,9 +270,8 @@ SDL_FS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     /* Allocate mixing buffer */
     this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
+    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        SDL_FS_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
@@ -328,7 +312,6 @@ SDL_FS_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = SDL_FS_WaitDevice;
     impl->GetDeviceBuf = SDL_FS_GetDeviceBuf;
     impl->CloseDevice = SDL_FS_CloseDevice;
-    impl->WaitDone = SDL_FS_WaitDone;
     impl->Deinitialize = SDL_FS_Deinitialize;
     impl->OnlyHasDefaultOutputDevice = 1;
 

+ 9 - 15
Engine/lib/sdl/src/audio/haiku/SDL_haikuaudio.cc

@@ -49,10 +49,11 @@ FillSound(void *device, void *stream, size_t len,
     SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
 
     /* Only do soemthing if audio is enabled */
-    if (!audio->enabled)
+    if (!SDL_AtomicGet(&audio->enabled)) {
         return;
+    }
 
-    if (!audio->paused) {
+    if (!SDL_AtomicGet(&audio->paused)) {
         if (audio->convert.needed) {
             SDL_LockMutex(audio->mixer_lock);
             (*audio->spec.callback) (audio->spec.userdata,
@@ -73,16 +74,11 @@ FillSound(void *device, void *stream, size_t len,
 static void
 HAIKUAUDIO_CloseDevice(_THIS)
 {
-    if (_this->hidden != NULL) {
-        if (_this->hidden->audio_obj) {
-            _this->hidden->audio_obj->Stop();
-            delete _this->hidden->audio_obj;
-            _this->hidden->audio_obj = NULL;
-        }
-
-        delete _this->hidden;
-        _this->hidden = NULL;
+    if (_this->hidden->audio_obj) {
+        _this->hidden->audio_obj->Stop();
+        delete _this->hidden->audio_obj;
     }
+    delete _this->hidden;
 }
 
 
@@ -122,10 +118,10 @@ HAIKUAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (_this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(_this->hidden, 0, (sizeof *_this->hidden));
+    SDL_zerop(_this->hidden);
 
     /* Parse the audio format and fill the Be raw audio format */
-    SDL_memset(&format, '\0', sizeof(media_raw_audio_format));
+    SDL_zero(format);
     format.byte_order = B_MEDIA_LITTLE_ENDIAN;
     format.frame_rate = (float) _this->spec.freq;
     format.channel_count = _this->spec.channels;        /* !!! FIXME: support > 2? */
@@ -176,7 +172,6 @@ HAIKUAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (!valid_datatype) {      /* shouldn't happen, but just in case... */
-        HAIKUAUDIO_CloseDevice(_this);
         return SDL_SetError("Unsupported audio format");
     }
 
@@ -195,7 +190,6 @@ HAIKUAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (_this->hidden->audio_obj->Start() == B_NO_ERROR) {
         _this->hidden->audio_obj->SetHasData(true);
     } else {
-        HAIKUAUDIO_CloseDevice(_this);
         return SDL_SetError("Unable to start Be audio");
     }
 

+ 17 - 20
Engine/lib/sdl/src/audio/nacl/SDL_naclaudio.c

@@ -27,7 +27,6 @@
 
 #include "SDL_audio.h"
 #include "SDL_mutex.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audiodev_c.h"
 
@@ -38,22 +37,20 @@
 #include "ppapi_simple/ps_event.h"
 
 /* The tag name used by NACL audio */
-#define NACLAUD_DRIVER_NAME         "nacl"
+#define NACLAUDIO_DRIVER_NAME         "nacl"
 
 #define SAMPLE_FRAME_COUNT 4096
 
 /* Audio driver functions */
-static int NACLAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture);
-static void NACLAUD_CloseDevice(_THIS);
 static void nacl_audio_callback(void* samples, uint32_t buffer_size, PP_TimeDelta latency, void* data);
 
 /* FIXME: Make use of latency if needed */
 static void nacl_audio_callback(void* samples, uint32_t buffer_size, PP_TimeDelta latency, void* data) {
     SDL_AudioDevice* _this = (SDL_AudioDevice*) data;
     
-    SDL_LockMutex(private->mutex);
+    SDL_LockMutex(private->mutex);  /* !!! FIXME: is this mutex necessary? */
 
-    if (_this->enabled && !_this->paused) {
+    if (SDL_AtomicGet(&_this->enabled) && !SDL_AtomicGet(&_this->paused)) {
         if (_this->convert.needed) {
             SDL_LockMutex(_this->mixer_lock);
             (*_this->spec.callback) (_this->spec.userdata,
@@ -68,13 +65,13 @@ static void nacl_audio_callback(void* samples, uint32_t buffer_size, PP_TimeDelt
             SDL_UnlockMutex(_this->mixer_lock);
         }
     } else {
-        SDL_memset(samples, 0, buffer_size);
+        SDL_memset(samples, _this->spec.silence, buffer_size);
     }
     
-    return;
+    SDL_UnlockMutex(private->mutex);
 }
 
-static void NACLAUD_CloseDevice(SDL_AudioDevice *device) {
+static void NACLAUDIO_CloseDevice(SDL_AudioDevice *device) {
     const PPB_Core *core = PSInterfaceCore();
     const PPB_Audio *ppb_audio = PSInterfaceAudio();
     SDL_PrivateAudioData *hidden = (SDL_PrivateAudioData *) device->hidden;
@@ -85,7 +82,7 @@ static void NACLAUD_CloseDevice(SDL_AudioDevice *device) {
 }
 
 static int
-NACLAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) {
+NACLAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) {
     PP_Instance instance = PSGetInstanceId();
     const PPB_Audio *ppb_audio = PSInterfaceAudio();
     const PPB_AudioConfig *ppb_audiocfg = PSInterfaceAudioConfig();
@@ -121,30 +118,30 @@ NACLAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) {
 }
 
 static int
-NACLAUD_Init(SDL_AudioDriverImpl * impl)
+NACLAUDIO_Init(SDL_AudioDriverImpl * impl)
 {
     if (PSGetInstanceId() == 0) {
         return 0;
     }
     
     /* Set the function pointers */
-    impl->OpenDevice = NACLAUD_OpenDevice;
-    impl->CloseDevice = NACLAUD_CloseDevice;
+    impl->OpenDevice = NACLAUDIO_OpenDevice;
+    impl->CloseDevice = NACLAUDIO_CloseDevice;
     impl->OnlyHasDefaultOutputDevice = 1;
     impl->ProvidesOwnCallbackThread = 1;
     /*
-     *    impl->WaitDevice = NACLAUD_WaitDevice;
-     *    impl->GetDeviceBuf = NACLAUD_GetDeviceBuf;
-     *    impl->PlayDevice = NACLAUD_PlayDevice;
-     *    impl->Deinitialize = NACLAUD_Deinitialize;
+     *    impl->WaitDevice = NACLAUDIO_WaitDevice;
+     *    impl->GetDeviceBuf = NACLAUDIO_GetDeviceBuf;
+     *    impl->PlayDevice = NACLAUDIO_PlayDevice;
+     *    impl->Deinitialize = NACLAUDIO_Deinitialize;
      */
     
     return 1;
 }
 
-AudioBootStrap NACLAUD_bootstrap = {
-    NACLAUD_DRIVER_NAME, "SDL NaCl Audio Driver",
-    NACLAUD_Init, 0
+AudioBootStrap NACLAUDIO_bootstrap = {
+    NACLAUDIO_DRIVER_NAME, "SDL NaCl Audio Driver",
+    NACLAUDIO_Init, 0
 };
 
 #endif /* SDL_AUDIO_DRIVER_NACL */

+ 116 - 50
Engine/lib/sdl/src/audio/nas/SDL_nasaudio.c

@@ -30,22 +30,21 @@
 #include "SDL_timer.h"
 #include "SDL_audio.h"
 #include "SDL_loadso.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_nasaudio.h"
 
-static struct SDL_PrivateAudioData *this2 = NULL;
-
-
 static void (*NAS_AuCloseServer) (AuServer *);
 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
+static void (*NAS_AuHandleEvents) (AuServer *);
 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
 static void (*NAS_AuSetElements)
   (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
 static void (*NAS_AuWriteElement)
   (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
+static AuUint32 (*NAS_AuReadElement)
+  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *);
 static AuServer *(*NAS_AuOpenServer)
   (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
@@ -80,10 +79,12 @@ load_nas_syms(void)
     SDL_NAS_SYM(AuCloseServer);
     SDL_NAS_SYM(AuNextEvent);
     SDL_NAS_SYM(AuDispatchEvent);
+    SDL_NAS_SYM(AuHandleEvents);
     SDL_NAS_SYM(AuCreateFlow);
     SDL_NAS_SYM(AuStartFlow);
     SDL_NAS_SYM(AuSetElements);
     SDL_NAS_SYM(AuWriteElement);
+    SDL_NAS_SYM(AuReadElement);
     SDL_NAS_SYM(AuOpenServer);
     SDL_NAS_SYM(AuRegisterEventHandler);
     return 0;
@@ -187,19 +188,53 @@ NAS_GetDeviceBuf(_THIS)
     return (this->hidden->mixbuf);
 }
 
+static int
+NAS_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
+    int retval;
+
+    while (SDL_TRUE) {
+        /* just keep the event queue moving and the server chattering. */
+        NAS_AuHandleEvents(h->aud);
+    
+        retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL);
+        /*printf("read %d capture bytes\n", (int) retval);*/
+        if (retval == 0) {
+            SDL_Delay(10);  /* don't burn the CPU if we're waiting for data. */
+        } else {
+            break;
+        }
+    }
+
+    return retval;
+}
+
+static void
+NAS_FlushCapture(_THIS)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
+    AuUint32 total = 0;
+    AuUint32 br;
+    Uint8 buf[512];
+
+    do {
+        /* just keep the event queue moving and the server chattering. */
+        NAS_AuHandleEvents(h->aud);
+        br = NAS_AuReadElement(h->aud, h->flow, 1, sizeof (buf), buf, NULL);
+        /*printf("flushed %d capture bytes\n", (int) br);*/
+        total += br;
+    } while ((br == sizeof (buf)) && (total < this->spec.size));
+}
+
 static void
 NAS_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->aud) {
-            NAS_AuCloseServer(this->hidden->aud);
-            this->hidden->aud = 0;
-        }
-        SDL_free(this->hidden);
-        this2 = this->hidden = NULL;
+    if (this->hidden->aud) {
+        NAS_AuCloseServer(this->hidden->aud);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static unsigned char
@@ -225,6 +260,12 @@ sdlformat_to_auformat(unsigned int fmt)
 static AuBool
 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
 {
+    SDL_AudioDevice *this = (SDL_AudioDevice *) hnd->data;
+    struct SDL_PrivateAudioData *h = this->hidden;
+    if (this->iscapture) {
+        return AuTrue;  /* we don't (currently) care about any of this for capture devices */
+    }
+
     switch (ev->type) {
     case AuEventTypeElementNotify:
         {
@@ -232,24 +273,24 @@ event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
 
             switch (event->kind) {
             case AuElementNotifyKindLowWater:
-                if (this2->buf_free >= 0) {
-                    this2->really += event->num_bytes;
-                    gettimeofday(&this2->last_tv, 0);
-                    this2->buf_free += event->num_bytes;
+                if (h->buf_free >= 0) {
+                    h->really += event->num_bytes;
+                    gettimeofday(&h->last_tv, 0);
+                    h->buf_free += event->num_bytes;
                 } else {
-                    this2->buf_free = event->num_bytes;
+                    h->buf_free = event->num_bytes;
                 }
                 break;
             case AuElementNotifyKindState:
                 switch (event->cur_state) {
                 case AuStatePause:
                     if (event->reason != AuReasonUser) {
-                        if (this2->buf_free >= 0) {
-                            this2->really += event->num_bytes;
-                            gettimeofday(&this2->last_tv, 0);
-                            this2->buf_free += event->num_bytes;
+                        if (h->buf_free >= 0) {
+                            h->really += event->num_bytes;
+                            gettimeofday(&h->last_tv, 0);
+                            h->buf_free += event->num_bytes;
                         } else {
-                            this2->buf_free = event->num_bytes;
+                            h->buf_free = event->num_bytes;
                         }
                     }
                     break;
@@ -261,15 +302,29 @@ event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
 }
 
 static AuDeviceID
-find_device(_THIS, int nch)
+find_device(_THIS)
 {
     /* These "Au" things are all macros, not functions... */
+    struct SDL_PrivateAudioData *h = this->hidden;
+    const unsigned int devicekind = this->iscapture ? AuComponentKindPhysicalInput : AuComponentKindPhysicalOutput;
+    const int numdevs = AuServerNumDevices(h->aud);
+    const int nch = this->spec.channels;
     int i;
-    for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
-        if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
-             AuComponentKindPhysicalOutput) &&
-            AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
-            return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
+
+    /* Try to find exact match on channels first... */
+    for (i = 0; i < numdevs; i++) {
+        const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
+        if ((AuDeviceKind(dev) == devicekind) && (AuDeviceNumTracks(dev) == nch)) {
+            return AuDeviceIdentifier(dev);
+        }
+    }
+
+    /* Take anything, then... */
+    for (i = 0; i < numdevs; i++) {
+        const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
+        if (AuDeviceKind(dev) == devicekind) {
+            this->spec.channels = AuDeviceNumTracks(dev);
+            return AuDeviceIdentifier(dev);
         }
     }
     return AuNone;
@@ -288,7 +343,7 @@ NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Try for a closest match on audio format */
     format = 0;
@@ -300,21 +355,18 @@ NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         }
     }
     if (format == 0) {
-        NAS_CloseDevice(this);
         return SDL_SetError("NAS: Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
 
     this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
     if (this->hidden->aud == 0) {
-        NAS_CloseDevice(this);
         return SDL_SetError("NAS: Couldn't open connection to NAS server");
     }
 
-    this->hidden->dev = find_device(this, this->spec.channels);
+    this->hidden->dev = find_device(this);
     if ((this->hidden->dev == AuNone)
         || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
-        NAS_CloseDevice(this);
         return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
     }
 
@@ -328,29 +380,38 @@ NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Calculate the final parameters for this audio specification */
     SDL_CalculateAudioSpec(&this->spec);
 
-    this2 = this->hidden;
+    if (iscapture) {
+        AuMakeElementImportDevice(elms, this->spec.freq, this->hidden->dev,
+                                  AuUnlimitedSamples, 0, NULL);
+        AuMakeElementExportClient(elms + 1, 0, this->spec.freq, format,
+                                  this->spec.channels, AuTrue, buffer_size,
+                                  buffer_size, 0, NULL);
+    } else {
+        AuMakeElementImportClient(elms, this->spec.freq, format,
+                                  this->spec.channels, AuTrue, buffer_size,
+                                  buffer_size / 4, 0, NULL);
+        AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
+                                  AuUnlimitedSamples, 0, NULL);
+    }
+
+    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue,
+                      2, elms, NULL);
 
-    AuMakeElementImportClient(elms, this->spec.freq, format,
-                              this->spec.channels, AuTrue, buffer_size,
-                              buffer_size / 4, 0, NULL);
-    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
-                              AuUnlimitedSamples, 0, NULL);
-    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
-                      NULL);
     NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
                                this->hidden->flow, event_handler,
-                               (AuPointer) NULL);
+                               (AuPointer) this);
 
     NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
 
     /* Allocate mixing buffer */
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        NAS_CloseDevice(this);
-        return SDL_OutOfMemory();
+    if (!iscapture) {
+        this->hidden->mixlen = this->spec.size;
+        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
+        if (this->hidden->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
     /* We're ready to rock and roll. :-) */
     return 0;
@@ -381,9 +442,14 @@ NAS_Init(SDL_AudioDriverImpl * impl)
     impl->PlayDevice = NAS_PlayDevice;
     impl->WaitDevice = NAS_WaitDevice;
     impl->GetDeviceBuf = NAS_GetDeviceBuf;
+    impl->CaptureFromDevice = NAS_CaptureFromDevice;
+    impl->FlushCapture = NAS_FlushCapture;
     impl->CloseDevice = NAS_CloseDevice;
     impl->Deinitialize = NAS_Deinitialize;
-    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
+
+    impl->OnlyHasDefaultOutputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }

+ 6 - 18
Engine/lib/sdl/src/audio/paudio/SDL_paudio.c

@@ -35,7 +35,6 @@
 #include "SDL_timer.h"
 #include "SDL_audio.h"
 #include "SDL_stdinc.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_paudio.h"
 
@@ -228,16 +227,11 @@ PAUDIO_GetDeviceBuf(_THIS)
 static void
 PAUDIO_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if (this->hidden->audio_fd >= 0) {
-            close(this->hidden->audio_fd);
-            this->hidden->audio_fd = -1;
-        }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->audio_fd >= 0) {
+        close(this->hidden->audio_fd);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -262,13 +256,12 @@ PAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Open the audio device */
     fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
     this->hidden->audio_fd = fd;
     if (fd < 0) {
-        PAUDIO_CloseDevice(this);
         return SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
     }
 
@@ -277,7 +270,6 @@ PAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
      * that we can have.
      */
     if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
-        PAUDIO_CloseDevice(this);
         return SDL_SetError("Couldn't get audio buffer information");
     }
 
@@ -391,7 +383,6 @@ PAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 #ifdef DEBUG_AUDIO
         fprintf(stderr, "Couldn't find any hardware audio formats\n");
 #endif
-        PAUDIO_CloseDevice(this);
         return SDL_SetError("Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
@@ -449,15 +440,13 @@ PAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (err != NULL) {
-        PAUDIO_CloseDevice(this);
         return SDL_SetError("Paudio: %s", err);
     }
 
     /* Allocate mixing buffer */
     this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
+    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        PAUDIO_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
@@ -492,7 +481,6 @@ PAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     paud_control.ioctl_request = AUDIO_START;
     paud_control.position = 0;
     if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
-        PAUDIO_CloseDevice(this);
 #ifdef DEBUG_AUDIO
         fprintf(stderr, "Can't start audio play\n");
 #endif

+ 22 - 41
Engine/lib/sdl/src/audio/psp/SDL_pspaudio.c

@@ -30,7 +30,6 @@
 #include "SDL_audio.h"
 #include "SDL_error.h"
 #include "SDL_timer.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audiodev_c.h"
 #include "../SDL_sysaudio.h"
@@ -40,10 +39,10 @@
 #include <pspthreadman.h>
 
 /* The tag name used by PSP audio */
-#define PSPAUD_DRIVER_NAME         "psp"
+#define PSPAUDIO_DRIVER_NAME         "psp"
 
 static int
-PSPAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+PSPAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
     int format, mixlen, i;
     this->hidden = (struct SDL_PrivateAudioData *)
@@ -51,7 +50,7 @@ PSPAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, sizeof(*this->hidden));
+    SDL_zerop(this->hidden);
     switch (this->spec.format & 0xff) {
         case 8:
         case 16:
@@ -66,20 +65,7 @@ PSPAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     this->spec.freq = 44100;
 
     /* Update the fragment size as size in bytes. */
-/*  SDL_CalculateAudioSpec(this->spec); MOD */
-    switch (this->spec.format) {
-    case AUDIO_U8:
-        this->spec.silence = 0x80;
-        break;
-    default:
-        this->spec.silence = 0x00;
-        break;
-    }
-    this->spec.size = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
-    this->spec.size *= this->spec.channels;
-    this->spec.size *= this->spec.samples;
-
-/* ========================================== */
+    SDL_CalculateAudioSpec(&this->spec);
 
     /* Allocate the mixing buffer.  Its size and starting address must
        be a multiple of 64 bytes.  Our sample count is already a multiple of
@@ -112,7 +98,7 @@ PSPAUD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     return 0;
 }
 
-static void PSPAUD_PlayDevice(_THIS)
+static void PSPAUDIO_PlayDevice(_THIS)
 {
     Uint8 *mixbuf = this->hidden->mixbufs[this->hidden->next_buffer];
 
@@ -126,28 +112,25 @@ static void PSPAUD_PlayDevice(_THIS)
 }
 
 /* This function waits until it is possible to write a full sound buffer */
-static void PSPAUD_WaitDevice(_THIS)
+static void PSPAUDIO_WaitDevice(_THIS)
 {
     /* Because we block when sending audio, there's no need for this function to do anything. */
 }
-static Uint8 *PSPAUD_GetDeviceBuf(_THIS)
+static Uint8 *PSPAUDIO_GetDeviceBuf(_THIS)
 {
     return this->hidden->mixbufs[this->hidden->next_buffer];
 }
 
-static void PSPAUD_CloseDevice(_THIS)
+static void PSPAUDIO_CloseDevice(_THIS)
 {
     if (this->hidden->channel >= 0) {
         sceAudioChRelease(this->hidden->channel);
-        this->hidden->channel = -1;
-    }
-
-    if (this->hidden->rawbuf != NULL) {
-        free(this->hidden->rawbuf);
-        this->hidden->rawbuf = NULL;
     }
+    free(this->hidden->rawbuf);  /* this uses memalign(), not SDL_malloc(). */
+    SDL_free(this->hidden);
 }
-static void PSPAUD_ThreadInit(_THIS)
+
+static void PSPAUDIO_ThreadInit(_THIS)
 {
     /* Increase the priority of this audio thread by 1 to put it
        ahead of other SDL threads. */
@@ -162,24 +145,22 @@ static void PSPAUD_ThreadInit(_THIS)
 
 
 static int
-PSPAUD_Init(SDL_AudioDriverImpl * impl)
+PSPAUDIO_Init(SDL_AudioDriverImpl * impl)
 {
-
     /* Set the function pointers */
-    impl->OpenDevice = PSPAUD_OpenDevice;
-    impl->PlayDevice = PSPAUD_PlayDevice;
-    impl->WaitDevice = PSPAUD_WaitDevice;
-    impl->GetDeviceBuf = PSPAUD_GetDeviceBuf;
-    impl->WaitDone = PSPAUD_WaitDevice;
-    impl->CloseDevice = PSPAUD_CloseDevice;
-    impl->ThreadInit = PSPAUD_ThreadInit;
+    impl->OpenDevice = PSPAUDIO_OpenDevice;
+    impl->PlayDevice = PSPAUDIO_PlayDevice;
+    impl->WaitDevice = PSPAUDIO_WaitDevice;
+    impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
+    impl->CloseDevice = PSPAUDIO_CloseDevice;
+    impl->ThreadInit = PSPAUDIO_ThreadInit;
 
     /* PSP audio device */
     impl->OnlyHasDefaultOutputDevice = 1;
 /*
     impl->HasCaptureSupport = 1;
 
-    impl->OnlyHasDefaultInputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
 */
     /*
     impl->DetectDevices = DSOUND_DetectDevices;
@@ -188,8 +169,8 @@ PSPAUD_Init(SDL_AudioDriverImpl * impl)
     return 1;   /* this audio target is available. */
 }
 
-AudioBootStrap PSPAUD_bootstrap = {
-    "psp", "PSP audio driver", PSPAUD_Init, 0
+AudioBootStrap PSPAUDIO_bootstrap = {
+    "psp", "PSP audio driver", PSPAUDIO_Init, 0
 };
 
  /* SDL_AUDI */

+ 132 - 59
Engine/lib/sdl/src/audio/pulseaudio/SDL_pulseaudio.c

@@ -42,10 +42,10 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_pulseaudio.h"
 #include "SDL_loadso.h"
+#include "../../thread/SDL_systhread.h"
 
 #if (PA_API_VERSION < 12)
 /** Return non-zero if the passed state is one of the connected states */
@@ -99,12 +99,19 @@ static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *,
     const pa_sample_spec *, const pa_channel_map *);
 static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *,
     const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *);
+static int (*PULSEAUDIO_pa_stream_connect_record) (pa_stream *, const char *,
+    const pa_buffer_attr *, pa_stream_flags_t);
 static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
 static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
+static size_t (*PULSEAUDIO_pa_stream_readable_size) (pa_stream *);
 static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
     pa_free_cb_t, int64_t, pa_seek_mode_t);
 static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
     pa_stream_success_cb_t, void *);
+static int (*PULSEAUDIO_pa_stream_peek) (pa_stream *, const void **, size_t *);
+static int (*PULSEAUDIO_pa_stream_drop) (pa_stream *);
+static pa_operation * (*PULSEAUDIO_pa_stream_flush)	(pa_stream *,
+    pa_stream_success_cb_t, void *);
 static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
 static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
 
@@ -205,11 +212,16 @@ load_pulseaudio_syms(void)
     SDL_PULSEAUDIO_SYM(pa_context_unref);
     SDL_PULSEAUDIO_SYM(pa_stream_new);
     SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
+    SDL_PULSEAUDIO_SYM(pa_stream_connect_record);
     SDL_PULSEAUDIO_SYM(pa_stream_get_state);
     SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
+    SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
     SDL_PULSEAUDIO_SYM(pa_stream_write);
     SDL_PULSEAUDIO_SYM(pa_stream_drain);
     SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
+    SDL_PULSEAUDIO_SYM(pa_stream_peek);
+    SDL_PULSEAUDIO_SYM(pa_stream_drop);
+    SDL_PULSEAUDIO_SYM(pa_stream_flush);
     SDL_PULSEAUDIO_SYM(pa_stream_unref);
     SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
     SDL_PULSEAUDIO_SYM(pa_strerror);
@@ -238,6 +250,12 @@ getAppName(void)
     return "SDL Application";  /* oh well. */
 }
 
+static void
+stream_operation_complete_no_op(pa_stream *s, int success, void *userdata)
+{
+    /* no-op for pa_stream_drain(), etc, to use for callback. */
+}
+
 static void
 WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
 {
@@ -325,7 +343,7 @@ PULSEAUDIO_WaitDevice(_THIS)
 {
     struct SDL_PrivateAudioData *h = this->hidden;
 
-    while (this->enabled) {
+    while (SDL_AtomicGet(&this->enabled)) {
         if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
             PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
             PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
@@ -343,66 +361,110 @@ PULSEAUDIO_PlayDevice(_THIS)
 {
     /* Write the audio data */
     struct SDL_PrivateAudioData *h = this->hidden;
-    if (this->enabled) {
+    if (SDL_AtomicGet(&this->enabled)) {
         if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
             SDL_OpenedAudioDeviceDisconnected(this);
         }
     }
 }
 
-static void
-stream_drain_complete(pa_stream *s, int success, void *userdata)
+static Uint8 *
+PULSEAUDIO_GetDeviceBuf(_THIS)
 {
-    /* no-op for pa_stream_drain() to use for callback. */
+    return (this->hidden->mixbuf);
 }
 
-static void
-PULSEAUDIO_WaitDone(_THIS)
+
+static int
+PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
 {
-    if (this->enabled) {
-        struct SDL_PrivateAudioData *h = this->hidden;
-        pa_operation *o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL);
-        if (o) {
-            while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
-                if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
-                    PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
-                    PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
-                    PULSEAUDIO_pa_operation_cancel(o);
-                    break;
-                }
+    struct SDL_PrivateAudioData *h = this->hidden;
+    const void *data = NULL;
+    size_t nbytes = 0;
+
+    while (SDL_AtomicGet(&this->enabled)) {
+        if (h->capturebuf != NULL) {
+            const int cpy = SDL_min(buflen, h->capturelen);
+            SDL_memcpy(buffer, h->capturebuf, cpy);
+            /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
+            h->capturebuf += cpy;
+            h->capturelen -= cpy;
+            if (h->capturelen == 0) {
+                h->capturebuf = NULL;
+                PULSEAUDIO_pa_stream_drop(h->stream);  /* done with this fragment. */
             }
-            PULSEAUDIO_pa_operation_unref(o);
+            return cpy;  /* new data, return it. */
+        }
+
+        if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
+            PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
+            PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
+            SDL_OpenedAudioDeviceDisconnected(this);
+            return -1;  /* uhoh, pulse failed! */
+        }
+
+        if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
+            continue;  /* no data available yet. */
+        }
+
+        /* a new fragment is available! */
+        PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
+        SDL_assert(nbytes > 0);
+        if (data == NULL) {  /* NULL==buffer had a hole. Ignore that. */
+            PULSEAUDIO_pa_stream_drop(h->stream);  /* drop this fragment. */
+        } else {
+            /* store this fragment's data, start feeding it to SDL. */
+            /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
+            h->capturebuf = (const Uint8 *) data;
+            h->capturelen = nbytes;
         }
     }
+
+    return -1;  /* not enabled? */
 }
 
+static void
+PULSEAUDIO_FlushCapture(_THIS)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
 
+    if (h->capturebuf != NULL) {
+        PULSEAUDIO_pa_stream_drop(h->stream);
+        h->capturebuf = NULL;
+        h->capturelen = 0;
+    }
 
-static Uint8 *
-PULSEAUDIO_GetDeviceBuf(_THIS)
-{
-    return (this->hidden->mixbuf);
+    WaitForPulseOperation(h->mainloop, PULSEAUDIO_pa_stream_flush(h->stream, stream_operation_complete_no_op, NULL));
 }
 
-
 static void
 PULSEAUDIO_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        SDL_free(this->hidden->device_name);
-        if (this->hidden->stream) {
-            PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
-            PULSEAUDIO_pa_stream_unref(this->hidden->stream);
+    if (this->hidden->stream) {
+        if (this->hidden->capturebuf != NULL) {
+            PULSEAUDIO_pa_stream_drop(this->hidden->stream);
         }
-        DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+        PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
+        PULSEAUDIO_pa_stream_unref(this->hidden->stream);
     }
+
+    DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden->device_name);
+    SDL_free(this->hidden);
 }
 
 static void
-DeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
+SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
+{
+    if (i) {
+        char **devname = (char **) data;
+        *devname = SDL_strdup(i->name);
+    }
+}
+
+static void
+SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
 {
     if (i) {
         char **devname = (char **) data;
@@ -411,7 +473,7 @@ DeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data
 }
 
 static SDL_bool
-FindDeviceName(struct SDL_PrivateAudioData *h, void *handle)
+FindDeviceName(struct SDL_PrivateAudioData *h, const int iscapture, void *handle)
 {
     const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
 
@@ -419,7 +481,16 @@ FindDeviceName(struct SDL_PrivateAudioData *h, void *handle)
         return SDL_TRUE;
     }
 
-    WaitForPulseOperation(h->mainloop, PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx, DeviceNameCallback, &h->device_name));
+    if (iscapture) {
+        WaitForPulseOperation(h->mainloop,
+            PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
+                SourceDeviceNameCallback, &h->device_name));
+    } else {
+        WaitForPulseOperation(h->mainloop,
+            PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
+                SinkDeviceNameCallback, &h->device_name));
+    }
+
     return (h->device_name != NULL);
 }
 
@@ -433,15 +504,15 @@ PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     pa_channel_map pacmap;
     pa_stream_flags_t flags = 0;
     int state = 0;
+    int rc = 0;
 
     /* Initialize all variables that we clean on shutdown */
-    this->hidden = (struct SDL_PrivateAudioData *)
+    h = this->hidden = (struct SDL_PrivateAudioData *)
         SDL_malloc((sizeof *this->hidden));
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
-    h = this->hidden;
+    SDL_zerop(this->hidden);
 
     paspec.format = PA_SAMPLE_INVALID;
 
@@ -482,7 +553,6 @@ PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         }
     }
     if (paspec.format == PA_SAMPLE_INVALID) {
-        PULSEAUDIO_CloseDevice(this);
         return SDL_SetError("Couldn't find any hardware audio formats");
     }
     this->spec.format = test_format;
@@ -494,13 +564,14 @@ PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     SDL_CalculateAudioSpec(&this->spec);
 
     /* Allocate mixing buffer */
-    h->mixlen = this->spec.size;
-    h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen);
-    if (h->mixbuf == NULL) {
-        PULSEAUDIO_CloseDevice(this);
-        return SDL_OutOfMemory();
+    if (!iscapture) {
+        h->mixlen = this->spec.size;
+        h->mixbuf = (Uint8 *) SDL_malloc(h->mixlen);
+        if (h->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
 
     paspec.channels = this->spec.channels;
     paspec.rate = this->spec.freq;
@@ -522,13 +593,11 @@ PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 #endif
 
     if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
-        PULSEAUDIO_CloseDevice(this);
         return SDL_SetError("Could not connect to PulseAudio server");
     }
 
-    if (!FindDeviceName(h, handle)) {
-        PULSEAUDIO_CloseDevice(this);
-        return SDL_SetError("Requested PulseAudio sink missing?");
+    if (!FindDeviceName(h, iscapture, handle)) {
+        return SDL_SetError("Requested PulseAudio sink/source missing?");
     }
 
     /* The SDL ALSA output hints us that we use Windows' channel mapping */
@@ -544,7 +613,6 @@ PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         );
 
     if (h->stream == NULL) {
-        PULSEAUDIO_CloseDevice(this);
         return SDL_SetError("Could not set up PulseAudio stream");
     }
 
@@ -554,20 +622,22 @@ PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         flags |= PA_STREAM_DONT_MOVE;
     }
 
-    if (PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags,
-            NULL, NULL) < 0) {
-        PULSEAUDIO_CloseDevice(this);
+    if (iscapture) {
+        rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
+    } else {
+        rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
+    }
+
+    if (rc < 0) {
         return SDL_SetError("Could not connect PulseAudio stream");
     }
 
     do {
         if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
-            PULSEAUDIO_CloseDevice(this);
             return SDL_SetError("pa_mainloop_iterate() failed");
         }
         state = PULSEAUDIO_pa_stream_get_state(h->stream);
         if (!PA_STREAM_IS_GOOD(state)) {
-            PULSEAUDIO_CloseDevice(this);
             return SDL_SetError("Could not connect PulseAudio stream");
         }
     } while (state != PA_STREAM_READY);
@@ -646,7 +716,7 @@ PULSEAUDIO_DetectDevices()
     WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
 
     /* ok, we have a sane list, let's set up hotplug notifications now... */
-    hotplug_thread = SDL_CreateThread(HotplugThread, "PulseHotplug", NULL);
+    hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
 }
 
 static void
@@ -684,8 +754,11 @@ PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = PULSEAUDIO_WaitDevice;
     impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
     impl->CloseDevice = PULSEAUDIO_CloseDevice;
-    impl->WaitDone = PULSEAUDIO_WaitDone;
     impl->Deinitialize = PULSEAUDIO_Deinitialize;
+    impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
+    impl->FlushCapture = PULSEAUDIO_FlushCapture;
+
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }

+ 3 - 0
Engine/lib/sdl/src/audio/pulseaudio/SDL_pulseaudio.h

@@ -42,6 +42,9 @@ struct SDL_PrivateAudioData
     /* Raw mixing buffer */
     Uint8 *mixbuf;
     int mixlen;
+
+    const Uint8 *capturebuf;
+    int capturelen;
 };
 
 #endif /* _SDL_pulseaudio_h */

+ 27 - 65
Engine/lib/sdl/src/audio/qsa/SDL_qsa_audio.c

@@ -45,7 +45,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_qsa_audio.h"
 
@@ -138,8 +137,7 @@ QSA_ThreadInit(_THIS)
 static void
 QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
 {
-    SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t));
-
+    SDL_zerop(cpars);
     cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
     cpars->mode = SND_PCM_MODE_BLOCK;
     cpars->start_mode = SND_PCM_START_DATA;
@@ -229,7 +227,7 @@ QSA_PlayDevice(_THIS)
     int towrite;
     void *pcmbuffer;
 
-    if ((!this->enabled) || (!this->hidden)) {
+    if (!SDL_AtomicGet(&this->enabled) || !this->hidden) {
         return;
     }
 
@@ -262,7 +260,7 @@ QSA_PlayDevice(_THIS)
                 continue;
             } else {
                 if ((errno == EINVAL) || (errno == EIO)) {
-                    SDL_memset(&cstatus, 0, sizeof(cstatus));
+                    SDL_zero(cstatus);
                     if (!this->hidden->iscapture) {
                         cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
                     } else {
@@ -305,7 +303,7 @@ QSA_PlayDevice(_THIS)
             towrite -= written;
             pcmbuffer += written * this->spec.channels;
         }
-    } while ((towrite > 0) && (this->enabled));
+    } while ((towrite > 0) && SDL_AtomicGet(&this->enabled));
 
     /* If we couldn't write, assume fatal error for now */
     if (towrite != 0) {
@@ -322,27 +320,21 @@ QSA_GetDeviceBuf(_THIS)
 static void
 QSA_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        if (this->hidden->audio_handle != NULL) {
-            if (!this->hidden->iscapture) {
-                /* Finish playing available samples */
-                snd_pcm_plugin_flush(this->hidden->audio_handle,
-                                     SND_PCM_CHANNEL_PLAYBACK);
-            } else {
-                /* Cancel unread samples during capture */
-                snd_pcm_plugin_flush(this->hidden->audio_handle,
-                                     SND_PCM_CHANNEL_CAPTURE);
-            }
-            snd_pcm_close(this->hidden->audio_handle);
-            this->hidden->audio_handle = NULL;
+    if (this->hidden->audio_handle != NULL) {
+        if (!this->hidden->iscapture) {
+            /* Finish playing available samples */
+            snd_pcm_plugin_flush(this->hidden->audio_handle,
+                                 SND_PCM_CHANNEL_PLAYBACK);
+        } else {
+            /* Cancel unread samples during capture */
+            snd_pcm_plugin_flush(this->hidden->audio_handle,
+                                 SND_PCM_CHANNEL_CAPTURE);
         }
-
-        SDL_FreeAudioMem(this->hidden->pcm_buf);
-        this->hidden->pcm_buf = NULL;
-
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+        snd_pcm_close(this->hidden->audio_handle);
     }
+
+    SDL_free(this->hidden->pcm_buf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -365,13 +357,13 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData));
+    SDL_zerop(this->hidden);
 
     /* Initialize channel transfer parameters to default */
     QSA_InitAudioParams(&cparams);
 
     /* Initialize channel direction: capture or playback */
-    this->hidden->iscapture = iscapture;
+    this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
 
     if (device != NULL) {
         /* Open requested audio device */
@@ -391,7 +383,6 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Check if requested device is opened */
     if (status < 0) {
         this->hidden->audio_handle = NULL;
-        QSA_CloseDevice(this);
         return QSA_SetError("snd_pcm_open", status);
     }
 
@@ -401,7 +392,6 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
             snd_pcm_plugin_set_disable(this->hidden->audio_handle,
                                        PLUGIN_DISABLE_MMAP);
         if (status < 0) {
-            QSA_CloseDevice(this);
             return QSA_SetError("snd_pcm_plugin_set_disable", status);
         }
     }
@@ -487,7 +477,6 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     /* assumes test_format not 0 on success */
     if (test_format == 0) {
-        QSA_CloseDevice(this);
         return SDL_SetError("QSA: Couldn't find any hardware audio formats");
     }
 
@@ -505,12 +494,11 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Setup the transfer parameters according to cparams */
     status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
     if (status < 0) {
-        QSA_CloseDevice(this);
         return QSA_SetError("snd_pcm_channel_params", status);
     }
 
     /* Make sure channel is setup right one last time */
-    SDL_memset(&csetup, 0, sizeof(csetup));
+    SDL_zero(csetup);
     if (!this->hidden->iscapture) {
         csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
     } else {
@@ -519,7 +507,6 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     /* Setup an audio channel */
     if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
-        QSA_CloseDevice(this);
         return SDL_SetError("QSA: Unable to setup channel");
     }
 
@@ -540,9 +527,8 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
      *  closest multiple)
      */
     this->hidden->pcm_buf =
-        (Uint8 *) SDL_AllocAudioMem(this->hidden->pcm_len);
+        (Uint8 *) SDL_malloc(this->hidden->pcm_len);
     if (this->hidden->pcm_buf == NULL) {
-        QSA_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     SDL_memset(this->hidden->pcm_buf, this->spec.silence,
@@ -560,7 +546,6 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (this->hidden->audio_fd < 0) {
-        QSA_CloseDevice(this);
         return QSA_SetError("snd_pcm_file_descriptor", status);
     }
 
@@ -578,7 +563,6 @@ QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (status < 0) {
-        QSA_CloseDevice(this);
         return QSA_SetError("snd_pcm_plugin_prepare", status);
     }
 
@@ -724,32 +708,13 @@ QSA_DetectDevices(void)
     }
 }
 
-static void
-QSA_WaitDone(_THIS)
-{
-    if (!this->hidden->iscapture) {
-        if (this->hidden->audio_handle != NULL) {
-            /* Wait till last fragment is played and stop channel */
-            snd_pcm_plugin_flush(this->hidden->audio_handle,
-                                 SND_PCM_CHANNEL_PLAYBACK);
-        }
-    } else {
-        if (this->hidden->audio_handle != NULL) {
-            /* Discard all unread data and stop channel */
-            snd_pcm_plugin_flush(this->hidden->audio_handle,
-                                 SND_PCM_CHANNEL_CAPTURE);
-        }
-    }
-}
-
 static void
 QSA_Deinitialize(void)
 {
     /* Clear devices array on shutdown */
-    SDL_memset(qsa_playback_device, 0x00,
-               sizeof(QSA_Device) * QSA_MAX_DEVICES);
-    SDL_memset(qsa_capture_device, 0x00,
-               sizeof(QSA_Device) * QSA_MAX_DEVICES);
+    /* !!! FIXME: we zero these on init...any reason to do it here? */
+    SDL_zero(qsa_playback_device);
+    SDL_zero(qsa_capture_device);
     qsa_playback_devices = 0;
     qsa_capture_devices = 0;
 }
@@ -761,10 +726,8 @@ QSA_Init(SDL_AudioDriverImpl * impl)
     int32_t status = 0;
 
     /* Clear devices array */
-    SDL_memset(qsa_playback_device, 0x00,
-               sizeof(QSA_Device) * QSA_MAX_DEVICES);
-    SDL_memset(qsa_capture_device, 0x00,
-               sizeof(QSA_Device) * QSA_MAX_DEVICES);
+    SDL_zero(qsa_playback_device);
+    SDL_zero(qsa_capture_device);
     qsa_playback_devices = 0;
     qsa_capture_devices = 0;
 
@@ -778,7 +741,6 @@ QSA_Init(SDL_AudioDriverImpl * impl)
     impl->PlayDevice = QSA_PlayDevice;
     impl->GetDeviceBuf = QSA_GetDeviceBuf;
     impl->CloseDevice = QSA_CloseDevice;
-    impl->WaitDone = QSA_WaitDone;
     impl->Deinitialize = QSA_Deinitialize;
     impl->LockDevice = NULL;
     impl->UnlockDevice = NULL;
@@ -788,7 +750,7 @@ QSA_Init(SDL_AudioDriverImpl * impl)
     impl->SkipMixerLock = 0;
     impl->HasCaptureSupport = 1;
     impl->OnlyHasDefaultOutputDevice = 0;
-    impl->OnlyHasDefaultInputDevice = 0;
+    impl->OnlyHasDefaultCaptureDevice = 0;
 
     /* Check if io-audio manager is running or not */
     status = snd_cards();

+ 1 - 1
Engine/lib/sdl/src/audio/qsa/SDL_qsa_audio.h

@@ -34,7 +34,7 @@
 struct SDL_PrivateAudioData
 {
     /* SDL capture state */
-    int iscapture;
+    SDL_bool iscapture;
 
     /* The audio device handle */
     int cardno;

+ 7 - 24
Engine/lib/sdl/src/audio/sndio/SDL_sndioaudio.c

@@ -36,7 +36,6 @@
 #include <unistd.h>
 
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_sndioaudio.h"
 
@@ -171,25 +170,15 @@ SNDIO_GetDeviceBuf(_THIS)
     return this->hidden->mixbuf;
 }
 
-static void
-SNDIO_WaitDone(_THIS)
-{
-    SNDIO_sio_stop(this->hidden->dev);
-}
-
 static void
 SNDIO_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        if ( this->hidden->dev != NULL ) {
-            SNDIO_sio_close(this->hidden->dev);
-            this->hidden->dev = NULL;
-        }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if ( this->hidden->dev != NULL ) {
+        SNDIO_sio_stop(this->hidden->dev);
+        SNDIO_sio_close(this->hidden->dev);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -204,13 +193,12 @@ SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, sizeof(*this->hidden));
+    SDL_zerop(this->hidden);
 
     this->hidden->mixlen = this->spec.size;
 
     /* !!! FIXME: SIO_DEVANY can be a specific device... */
     if ((this->hidden->dev = SNDIO_sio_open(SIO_DEVANY, SIO_PLAY, 0)) == NULL) {
-        SNDIO_CloseDevice(this);
         return SDL_SetError("sio_open() failed");
     }
 
@@ -233,7 +221,6 @@ SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
                 continue;
             }
             if (SNDIO_sio_getpar(this->hidden->dev, &par) == 0) {
-                SNDIO_CloseDevice(this);
                 return SDL_SetError("sio_getpar() failed");
             }
             if (par.bps != SIO_BPS(par.bits)) {
@@ -248,7 +235,6 @@ SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (status < 0) {
-        SNDIO_CloseDevice(this);
         return SDL_SetError("sndio: Couldn't find any hardware audio formats");
     }
 
@@ -269,7 +255,6 @@ SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     else if ((par.bps == 1) && (!par.sig))
         this->spec.format = AUDIO_U8;
     else {
-        SNDIO_CloseDevice(this);
         return SDL_SetError("sndio: Got unsupported hardware audio format.");
     }
 
@@ -282,9 +267,8 @@ SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     /* Allocate mixing buffer */
     this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
+    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        SNDIO_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
@@ -315,7 +299,6 @@ SNDIO_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = SNDIO_WaitDevice;
     impl->PlayDevice = SNDIO_PlayDevice;
     impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
-    impl->WaitDone = SNDIO_WaitDone;
     impl->CloseDevice = SNDIO_CloseDevice;
     impl->Deinitialize = SNDIO_Deinitialize;
     impl->OnlyHasDefaultOutputDevice = 1;  /* !!! FIXME: sndio can handle multiple devices. */

+ 7 - 14
Engine/lib/sdl/src/audio/sun/SDL_sunaudio.c

@@ -40,7 +40,6 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
-#include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audiodev_c.h"
 #include "SDL_sunaudio.h"
@@ -183,18 +182,12 @@ SUNAUDIO_GetDeviceBuf(_THIS)
 static void
 SUNAUDIO_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-        SDL_free(this->hidden->ulaw_buf);
-        this->hidden->ulaw_buf = NULL;
-        if (this->hidden->audio_fd >= 0) {
-            close(this->hidden->audio_fd);
-            this->hidden->audio_fd = -1;
-        }
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    SDL_free(this->hidden->ulaw_buf);
+    if (this->hidden->audio_fd >= 0) {
+        close(this->hidden->audio_fd);
     }
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -219,7 +212,7 @@ SUNAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Open the audio device */
     this->hidden->audio_fd = open(devname, flags, 0);
@@ -340,7 +333,7 @@ SUNAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     SDL_CalculateAudioSpec(&this->spec);
 
     /* Allocate mixing buffer */
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->spec.size);
+    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
     if (this->hidden->mixbuf == NULL) {
         return SDL_OutOfMemory();
     }

+ 121 - 63
Engine/lib/sdl/src/audio/winmm/SDL_winmm.c

@@ -27,6 +27,7 @@
 #include "../../core/windows/SDL_windows.h"
 #include <mmsystem.h>
 
+#include "SDL_assert.h"
 #include "SDL_timer.h"
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
@@ -40,11 +41,11 @@
 static void DetectWave##typ##Devs(void) { \
     const UINT iscapture = iscap ? 1 : 0; \
     const UINT devcount = wave##typ##GetNumDevs(); \
-    capstyp caps; \
+    capstyp##2W caps; \
     UINT i; \
     for (i = 0; i < devcount; i++) { \
-        if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
-            char *name = WIN_StringToUTF8(caps.szPname); \
+	if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
+            char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \
             if (name != NULL) { \
                 SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \
                 SDL_free(name); \
@@ -134,63 +135,87 @@ WINMM_PlayDevice(_THIS)
     this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
 }
 
-static void
-WINMM_WaitDone(_THIS)
+static int
+WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
 {
-    int i, left;
+    const int nextbuf = this->hidden->next_buffer;
+    MMRESULT result;
 
-    do {
-        left = NUM_BUFFERS;
-        for (i = 0; i < NUM_BUFFERS; ++i) {
-            if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
-                --left;
-            }
-        }
-        if (left > 0) {
-            SDL_Delay(100);
-        }
-    } while (left > 0);
+    SDL_assert(buflen == this->spec.size);
+
+    /* Wait for an audio chunk to finish */
+    WaitForSingleObject(this->hidden->audio_sem, INFINITE);
+
+    /* Copy it to caller's buffer... */
+    SDL_memcpy(buffer, this->hidden->wavebuf[nextbuf].lpData, this->spec.size);
+
+    /* requeue the buffer that just finished. */
+    result = waveInAddBuffer(this->hidden->hin,
+                             &this->hidden->wavebuf[nextbuf],
+                             sizeof (this->hidden->wavebuf[nextbuf]));
+    if (result != MMSYSERR_NOERROR) {
+        return -1;  /* uhoh! Disable the device. */
+    }
+
+    /* queue the next buffer in sequence, next time. */
+    this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
+    return this->spec.size;
+}
+
+static void
+WINMM_FlushCapture(_THIS)
+{
+    /* Wait for an audio chunk to finish */
+    if (WaitForSingleObject(this->hidden->audio_sem, 0) == WAIT_OBJECT_0) {
+        const int nextbuf = this->hidden->next_buffer;
+        /* requeue the buffer that just finished without reading from it. */
+        waveInAddBuffer(this->hidden->hin,
+                        &this->hidden->wavebuf[nextbuf],
+                        sizeof (this->hidden->wavebuf[nextbuf]));
+        this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
+    }
 }
 
 static void
 WINMM_CloseDevice(_THIS)
 {
-    /* Close up audio */
-    if (this->hidden != NULL) {
-        int i;
+    int i;
 
-        if (this->hidden->audio_sem) {
-            CloseHandle(this->hidden->audio_sem);
-            this->hidden->audio_sem = 0;
-        }
+    if (this->hidden->hout) {
+        waveOutReset(this->hidden->hout);
 
         /* Clean up mixing buffers */
         for (i = 0; i < NUM_BUFFERS; ++i) {
             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
                 waveOutUnprepareHeader(this->hidden->hout,
                                        &this->hidden->wavebuf[i],
-                                       sizeof(this->hidden->wavebuf[i]));
-                this->hidden->wavebuf[i].dwUser = 0xFFFF;
+                                       sizeof (this->hidden->wavebuf[i]));
             }
         }
 
-        /* Free raw mixing buffer */
-        SDL_free(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
+        waveOutClose(this->hidden->hout);
+    }
 
-        if (this->hidden->hin) {
-            waveInClose(this->hidden->hin);
-            this->hidden->hin = 0;
-        }
+    if (this->hidden->hin) {
+        waveInReset(this->hidden->hin);
 
-        if (this->hidden->hout) {
-            waveOutClose(this->hidden->hout);
-            this->hidden->hout = 0;
+        /* Clean up mixing buffers */
+        for (i = 0; i < NUM_BUFFERS; ++i) {
+            if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
+                waveInUnprepareHeader(this->hidden->hin,
+                                       &this->hidden->wavebuf[i],
+                                       sizeof (this->hidden->wavebuf[i]));
+            }
         }
+        waveInClose(this->hidden->hin);
+    }
 
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (this->hidden->audio_sem) {
+        CloseHandle(this->hidden->audio_sem);
     }
+
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static SDL_bool
@@ -239,7 +264,7 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     /* Initialize the wavebuf structures for closing */
     for (i = 0; i < NUM_BUFFERS; ++i)
@@ -269,7 +294,6 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (!valid_datatype) {
-        WINMM_CloseDevice(this);
         return SDL_SetError("Unsupported audio format");
     }
 
@@ -281,36 +305,45 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         result = waveInOpen(&this->hidden->hin, devId, &waveformat,
                              (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
                              CALLBACK_FUNCTION);
+        if (result != MMSYSERR_NOERROR) {
+            return SetMMerror("waveInOpen()", result);
+        }
     } else {
         result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
                              (DWORD_PTR) FillSound, (DWORD_PTR) this,
                              CALLBACK_FUNCTION);
+        if (result != MMSYSERR_NOERROR) {
+            return SetMMerror("waveOutOpen()", result);
+        }
     }
 
-    if (result != MMSYSERR_NOERROR) {
-        WINMM_CloseDevice(this);
-        return SetMMerror("waveOutOpen()", result);
-    }
 #ifdef SOUND_DEBUG
     /* Check the sound device we retrieved */
     {
-        WAVEOUTCAPS caps;
-
-        result = waveOutGetDevCaps((UINT) this->hidden->hout,
-                                   &caps, sizeof(caps));
-        if (result != MMSYSERR_NOERROR) {
-            WINMM_CloseDevice(this);
-            return SetMMerror("waveOutGetDevCaps()", result);
+        if (iscapture) {
+            WAVEINCAPS caps;
+            result = waveInGetDevCaps((UINT) this->hidden->hout,
+                                      &caps, sizeof (caps));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveInGetDevCaps()", result);
+            }
+            printf("Audio device: %s\n", caps.szPname);
+        } else {
+            WAVEOUTCAPS caps;
+            result = waveOutGetDevCaps((UINT) this->hidden->hout,
+                                       &caps, sizeof(caps));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveOutGetDevCaps()", result);
+            }
+            printf("Audio device: %s\n", caps.szPname);
         }
-        printf("Audio device: %s\n", caps.szPname);
     }
 #endif
 
     /* Create the audio buffer semaphore */
     this->hidden->audio_sem =
-        CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
+		CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
     if (this->hidden->audio_sem == NULL) {
-        WINMM_CloseDevice(this);
         return SDL_SetError("Couldn't create semaphore");
     }
 
@@ -318,22 +351,44 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     this->hidden->mixbuf =
         (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
     if (this->hidden->mixbuf == NULL) {
-        WINMM_CloseDevice(this);
         return SDL_OutOfMemory();
     }
+
+    SDL_zero(this->hidden->wavebuf);
     for (i = 0; i < NUM_BUFFERS; ++i) {
-        SDL_memset(&this->hidden->wavebuf[i], 0,
-                   sizeof(this->hidden->wavebuf[i]));
         this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
         this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
         this->hidden->wavebuf[i].lpData =
             (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
-        result = waveOutPrepareHeader(this->hidden->hout,
-                                      &this->hidden->wavebuf[i],
-                                      sizeof(this->hidden->wavebuf[i]));
+
+        if (iscapture) {
+            result = waveInPrepareHeader(this->hidden->hin,
+                                          &this->hidden->wavebuf[i],
+                                          sizeof(this->hidden->wavebuf[i]));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveInPrepareHeader()", result);
+            }
+
+            result = waveInAddBuffer(this->hidden->hin,
+                                     &this->hidden->wavebuf[i],
+                                     sizeof(this->hidden->wavebuf[i]));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveInAddBuffer()", result);
+            }
+        } else {
+            result = waveOutPrepareHeader(this->hidden->hout,
+                                          &this->hidden->wavebuf[i],
+                                          sizeof(this->hidden->wavebuf[i]));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveOutPrepareHeader()", result);
+            }
+        }
+    }
+
+    if (iscapture) {
+        result = waveInStart(this->hidden->hin);
         if (result != MMSYSERR_NOERROR) {
-            WINMM_CloseDevice(this);
-            return SetMMerror("waveOutPrepareHeader()", result);
+            return SetMMerror("waveInStart()", result);
         }
     }
 
@@ -349,10 +404,13 @@ WINMM_Init(SDL_AudioDriverImpl * impl)
     impl->OpenDevice = WINMM_OpenDevice;
     impl->PlayDevice = WINMM_PlayDevice;
     impl->WaitDevice = WINMM_WaitDevice;
-    impl->WaitDone = WINMM_WaitDone;
     impl->GetDeviceBuf = WINMM_GetDeviceBuf;
+    impl->CaptureFromDevice = WINMM_CaptureFromDevice;
+    impl->FlushCapture = WINMM_FlushCapture;
     impl->CloseDevice = WINMM_CloseDevice;
 
+    impl->HasCaptureSupport = SDL_TRUE;
+
     return 1;   /* this audio target is available. */
 }
 

+ 30 - 54
Engine/lib/sdl/src/audio/xaudio2/SDL_xaudio2.c

@@ -195,7 +195,7 @@ XAUDIO2_PlayDevice(_THIS)
     IXAudio2SourceVoice *source = this->hidden->source;
     HRESULT result = S_OK;
 
-    if (!this->enabled) { /* shutting down? */
+    if (!SDL_AtomicGet(&this->enabled)) { /* shutting down? */
         return;
     }
 
@@ -226,64 +226,47 @@ XAUDIO2_PlayDevice(_THIS)
 static void
 XAUDIO2_WaitDevice(_THIS)
 {
-    if (this->enabled) {
+    if (SDL_AtomicGet(&this->enabled)) {
         SDL_SemWait(this->hidden->semaphore);
     }
 }
 
 static void
-XAUDIO2_WaitDone(_THIS)
+XAUDIO2_PrepareToClose(_THIS)
 {
     IXAudio2SourceVoice *source = this->hidden->source;
-    XAUDIO2_VOICE_STATE state;
-    SDL_assert(!this->enabled);  /* flag that stops playing. */
-    IXAudio2SourceVoice_Discontinuity(source);
-#if SDL_XAUDIO2_WIN8
-    IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
-#else
-    IXAudio2SourceVoice_GetState(source, &state);
-#endif
-    while (state.BuffersQueued > 0) {
-        SDL_SemWait(this->hidden->semaphore);
-#if SDL_XAUDIO2_WIN8
-        IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
-#else
-        IXAudio2SourceVoice_GetState(source, &state);
-#endif
+    if (source) {
+        IXAudio2SourceVoice_Discontinuity(source);
     }
 }
 
-
 static void
 XAUDIO2_CloseDevice(_THIS)
 {
-    if (this->hidden != NULL) {
-        IXAudio2 *ixa2 = this->hidden->ixa2;
-        IXAudio2SourceVoice *source = this->hidden->source;
-        IXAudio2MasteringVoice *mastering = this->hidden->mastering;
-
-        if (source != NULL) {
-            IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
-            IXAudio2SourceVoice_FlushSourceBuffers(source);
-            IXAudio2SourceVoice_DestroyVoice(source);
-        }
-        if (ixa2 != NULL) {
-            IXAudio2_StopEngine(ixa2);
-        }
-        if (mastering != NULL) {
-            IXAudio2MasteringVoice_DestroyVoice(mastering);
-        }
-        if (ixa2 != NULL) {
-            IXAudio2_Release(ixa2);
-        }
-        SDL_free(this->hidden->mixbuf);
-        if (this->hidden->semaphore != NULL) {
-            SDL_DestroySemaphore(this->hidden->semaphore);
-        }
+    IXAudio2 *ixa2 = this->hidden->ixa2;
+    IXAudio2SourceVoice *source = this->hidden->source;
+    IXAudio2MasteringVoice *mastering = this->hidden->mastering;
 
-        SDL_free(this->hidden);
-        this->hidden = NULL;
+    if (source != NULL) {
+        IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
+        IXAudio2SourceVoice_FlushSourceBuffers(source);
+        IXAudio2SourceVoice_DestroyVoice(source);
+    }
+    if (ixa2 != NULL) {
+        IXAudio2_StopEngine(ixa2);
     }
+    if (mastering != NULL) {
+        IXAudio2MasteringVoice_DestroyVoice(mastering);
+    }
+    if (ixa2 != NULL) {
+        IXAudio2_Release(ixa2);
+    }
+    if (this->hidden->semaphore != NULL) {
+        SDL_DestroySemaphore(this->hidden->semaphore);
+    }
+
+    SDL_free(this->hidden->mixbuf);
+    SDL_free(this->hidden);
 }
 
 static int
@@ -345,12 +328,11 @@ XAUDIO2_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         IXAudio2_Release(ixa2);
         return SDL_OutOfMemory();
     }
-    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+    SDL_zerop(this->hidden);
 
     this->hidden->ixa2 = ixa2;
     this->hidden->semaphore = SDL_CreateSemaphore(1);
     if (this->hidden->semaphore == NULL) {
-        XAUDIO2_CloseDevice(this);
         return SDL_SetError("XAudio2: CreateSemaphore() failed!");
     }
 
@@ -368,7 +350,6 @@ XAUDIO2_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     if (!valid_format) {
-        XAUDIO2_CloseDevice(this);
         return SDL_SetError("XAudio2: Unsupported audio format");
     }
 
@@ -379,11 +360,10 @@ XAUDIO2_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     this->hidden->mixlen = this->spec.size;
     this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        XAUDIO2_CloseDevice(this);
         return SDL_OutOfMemory();
     }
     this->hidden->nextbuf = this->hidden->mixbuf;
-    SDL_memset(this->hidden->mixbuf, 0, 2 * this->hidden->mixlen);
+    SDL_memset(this->hidden->mixbuf, this->spec.silence, 2 * this->hidden->mixlen);
 
     /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On
        Xbox360, this means 5.1 output, but on Windows, it means "figure out
@@ -401,7 +381,6 @@ XAUDIO2_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
                                            this->spec.freq, 0, devId, NULL);
 #endif
     if (result != S_OK) {
-        XAUDIO2_CloseDevice(this);
         return SDL_SetError("XAudio2: Couldn't create mastering voice");
     }
 
@@ -436,7 +415,6 @@ XAUDIO2_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
 #endif
     if (result != S_OK) {
-        XAUDIO2_CloseDevice(this);
         return SDL_SetError("XAudio2: Couldn't create source voice");
     }
     this->hidden->source = source;
@@ -444,13 +422,11 @@ XAUDIO2_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     /* Start everything playing! */
     result = IXAudio2_StartEngine(ixa2);
     if (result != S_OK) {
-        XAUDIO2_CloseDevice(this);
         return SDL_SetError("XAudio2: Couldn't start engine");
     }
 
     result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW);
     if (result != S_OK) {
-        XAUDIO2_CloseDevice(this);
         return SDL_SetError("XAudio2: Couldn't start source voice");
     }
 
@@ -499,7 +475,7 @@ XAUDIO2_Init(SDL_AudioDriverImpl * impl)
     impl->OpenDevice = XAUDIO2_OpenDevice;
     impl->PlayDevice = XAUDIO2_PlayDevice;
     impl->WaitDevice = XAUDIO2_WaitDevice;
-    impl->WaitDone = XAUDIO2_WaitDone;
+    impl->PrepareToClose = XAUDIO2_PrepareToClose;
     impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
     impl->CloseDevice = XAUDIO2_CloseDevice;
     impl->Deinitialize = XAUDIO2_Deinitialize;

+ 242 - 90
Engine/lib/sdl/src/core/android/SDL_android.c

@@ -71,10 +71,14 @@ static jclass mActivityClass;
 
 /* method signatures */
 static jmethodID midGetNativeSurface;
-static jmethodID midAudioInit;
+static jmethodID midAudioOpen;
 static jmethodID midAudioWriteShortBuffer;
 static jmethodID midAudioWriteByteBuffer;
-static jmethodID midAudioQuit;
+static jmethodID midAudioClose;
+static jmethodID midCaptureOpen;
+static jmethodID midCaptureReadShortBuffer;
+static jmethodID midCaptureReadByteBuffer;
+static jmethodID midCaptureClose;
 static jmethodID midPollInputDevices;
 
 /* Accelerometer data storage */
@@ -118,34 +122,45 @@ JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls)
 
     midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "getNativeSurface","()Landroid/view/Surface;");
-    midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
-                                "audioInit", "(IZZI)I");
+    midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "audioOpen", "(IZZI)I");
     midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "audioWriteShortBuffer", "([S)V");
     midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "audioWriteByteBuffer", "([B)V");
-    midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
-                                "audioQuit", "()V");
+    midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "audioClose", "()V");
+    midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "captureOpen", "(IZZI)I");
+    midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "captureReadShortBuffer", "([SZ)I");
+    midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "captureReadByteBuffer", "([BZ)I");
+    midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "captureClose", "()V");
     midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "pollInputDevices", "()V");
 
     bHasNewData = SDL_FALSE;
 
-    if (!midGetNativeSurface || !midAudioInit ||
-       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit || !midPollInputDevices) {
+    if (!midGetNativeSurface ||
+       !midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
+       !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose ||
+       !midPollInputDevices) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
     }
     __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
 }
 
 /* Drop file */
-void Java_org_libsdl_app_SDLActivity_onNativeDropFile(
+JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeDropFile(
                                     JNIEnv* env, jclass jcls,
                                     jstring filename)
 {
     const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
-    SDL_SendDropFile(path);
+    SDL_SendDropFile(NULL, path);
     (*env)->ReleaseStringUTFChars(env, filename, path);
+    SDL_SendDropComplete(NULL);
 }
 
 /* Resize */
@@ -555,11 +570,15 @@ int Android_JNI_SetupThread(void)
 static jboolean audioBuffer16Bit = JNI_FALSE;
 static jobject audioBuffer = NULL;
 static void* audioBufferPinned = NULL;
+static jboolean captureBuffer16Bit = JNI_FALSE;
+static jobject captureBuffer = NULL;
 
-int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
+int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
 {
     jboolean audioBufferStereo;
     int audioBufferFrames;
+    jobject jbufobj = NULL;
+    jboolean isCopy;
 
     JNIEnv *env = Android_JNI_GetEnv();
 
@@ -568,14 +587,24 @@ int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, i
     }
     Android_JNI_SetupThread();
 
-    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
-    audioBuffer16Bit = is16Bit;
     audioBufferStereo = channelCount > 1;
 
-    if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
-        /* Error during audio initialization */
-        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
-        return 0;
+    if (iscapture) {
+        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
+        captureBuffer16Bit = is16Bit;
+        if ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
+            /* Error during audio initialization */
+            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
+            return 0;
+        }
+    } else {
+        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
+        audioBuffer16Bit = is16Bit;
+        if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
+            /* Error during audio initialization */
+            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
+            return 0;
+        }
     }
 
     /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
@@ -584,31 +613,43 @@ int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, i
     if (is16Bit) {
         jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
         if (audioBufferLocal) {
-            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
+            jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
             (*env)->DeleteLocalRef(env, audioBufferLocal);
         }
     }
     else {
         jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
         if (audioBufferLocal) {
-            audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
+            jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
             (*env)->DeleteLocalRef(env, audioBufferLocal);
         }
     }
 
-    if (audioBuffer == NULL) {
+    if (jbufobj == NULL) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
         return 0;
     }
 
-    jboolean isCopy = JNI_FALSE;
-    if (audioBuffer16Bit) {
-        audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
+    if (iscapture) {
+        captureBuffer = jbufobj;
+    } else {
+        audioBuffer = jbufobj;
+    }
+
+    isCopy = JNI_FALSE;
+
+    if (is16Bit) {
+        if (!iscapture) {
+            audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
+        }
         audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
     } else {
-        audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
+        if (!iscapture) {
+            audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
+        }
         audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
     }
+
     if (audioBufferStereo) {
         audioBufferFrames /= 2;
     }
@@ -636,16 +677,71 @@ void Android_JNI_WriteAudioBuffer(void)
     /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
 }
 
-void Android_JNI_CloseAudioDevice(void)
+int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
 {
     JNIEnv *env = Android_JNI_GetEnv();
+    jboolean isCopy = JNI_FALSE;
+    jint br;
+
+    if (captureBuffer16Bit) {
+        SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
+        br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
+        if (br > 0) {
+            jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
+            br *= 2;
+            SDL_memcpy(buffer, ptr, br);
+            (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
+        }
+    } else {
+        SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
+        br = (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
+        if (br > 0) {
+            jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
+            SDL_memcpy(buffer, ptr, br);
+            (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
+        }
+    }
 
-    (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
+    return (int) br;
+}
 
-    if (audioBuffer) {
-        (*env)->DeleteGlobalRef(env, audioBuffer);
-        audioBuffer = NULL;
-        audioBufferPinned = NULL;
+void Android_JNI_FlushCapturedAudio(void)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    #if 0  /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
+    if (captureBuffer16Bit) {
+        const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
+        while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
+    } else {
+        const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
+        while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
+    }
+    #else
+    if (captureBuffer16Bit) {
+        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
+    } else {
+        (*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
+    }
+    #endif
+}
+
+void Android_JNI_CloseAudioDevice(const int iscapture)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+
+    if (iscapture) {
+        (*env)->CallStaticVoidMethod(env, mActivityClass, midCaptureClose);
+        if (captureBuffer) {
+            (*env)->DeleteGlobalRef(env, captureBuffer);
+            captureBuffer = NULL;
+        }
+    } else {
+        (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioClose);
+        if (audioBuffer) {
+            (*env)->DeleteGlobalRef(env, audioBuffer);
+            audioBuffer = NULL;
+            audioBufferPinned = NULL;
+        }
     }
 }
 
@@ -653,10 +749,12 @@ void Android_JNI_CloseAudioDevice(void)
 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
 {
-    SDL_assert(LocalReferenceHolder_IsActive());
     JNIEnv *mEnv = Android_JNI_GetEnv();
+    jthrowable exception;
 
-    jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
+    SDL_assert(LocalReferenceHolder_IsActive());
+
+    exception = (*mEnv)->ExceptionOccurred(mEnv);
     if (exception != NULL) {
         jmethodID mid;
 
@@ -666,13 +764,16 @@ static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
         if (!silent) {
             jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
             jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
+            jstring exceptionName;
+            const char* exceptionNameUTF8;
+            jstring exceptionMessage;
 
             mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
-            jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
-            const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
+            exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
+            exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
 
             mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
-            jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
+            exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
 
             if (exceptionMessage != NULL) {
                 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
@@ -855,6 +956,7 @@ int Android_JNI_FileOpen(SDL_RWops* ctx,
     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
     JNIEnv *mEnv = Android_JNI_GetEnv();
     int retval;
+    jstring fileNameJString;
 
     if (!LocalReferenceHolder_Init(&refs, mEnv)) {
         LocalReferenceHolder_Cleanup(&refs);        
@@ -866,7 +968,7 @@ int Android_JNI_FileOpen(SDL_RWops* ctx,
         return -1;
     }
 
-    jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
+    fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
     ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
     ctx->hidden.androidio.inputStreamRef = NULL;
     ctx->hidden.androidio.readableByteChannelRef = NULL;
@@ -885,10 +987,11 @@ size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
 
     if (ctx->hidden.androidio.assetFileDescriptorRef) {
         size_t bytesMax = size * maxnum;
+        size_t result;
         if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
             bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
         }
-        size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
+        result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
         if (result > 0) {
             ctx->hidden.androidio.position += result;
             LocalReferenceHolder_Cleanup(&refs);
@@ -900,19 +1003,23 @@ size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
         jlong bytesRemaining = (jlong) (size * maxnum);
         jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
         int bytesRead = 0;
+        JNIEnv *mEnv;
+        jobject readableByteChannel;
+        jmethodID readMethod;
+        jobject byteBuffer;
 
         /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
         if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
 
-        JNIEnv *mEnv = Android_JNI_GetEnv();
+        mEnv = Android_JNI_GetEnv();
         if (!LocalReferenceHolder_Init(&refs, mEnv)) {
             LocalReferenceHolder_Cleanup(&refs);            
             return 0;
         }
 
-        jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
-        jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
-        jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
+        readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
+        readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
+        byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
 
         while (bytesRemaining > 0) {
             /* result = readableByteChannel.read(...); */
@@ -1002,6 +1109,7 @@ Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
 Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
 {
     if (ctx->hidden.androidio.assetFileDescriptorRef) {
+        off_t ret;
         switch (whence) {
             case RW_SEEK_SET:
                 if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
@@ -1020,11 +1128,12 @@ Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
         }
         whence = SEEK_SET;
 
-        off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
+        ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
         if (ret == -1) return -1;
         ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
     } else {
         Sint64 newPosition;
+        Sint64 movement;
 
         switch (whence) {
             case RW_SEEK_SET:
@@ -1048,17 +1157,18 @@ Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
             newPosition = ctx->hidden.androidio.size;
         }
 
-        Sint64 movement = newPosition - ctx->hidden.androidio.position;
+        movement = newPosition - ctx->hidden.androidio.position;
         if (movement > 0) {
             unsigned char buffer[4096];
 
             /* The easy case where we're seeking forwards */
             while (movement > 0) {
                 Sint64 amount = sizeof (buffer);
+                size_t result;
                 if (amount > movement) {
                     amount = movement;
                 }
-                size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
+                result = Android_JNI_FileRead(ctx, buffer, 1, amount);
                 if (result <= 0) {
                     /* Failed to read/skip the required amount, so fail */
                     return -1;
@@ -1091,21 +1201,23 @@ static jobject Android_JNI_GetSystemServiceObject(const char* name)
     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
     JNIEnv* env = Android_JNI_GetEnv();
     jobject retval = NULL;
+    jstring service;
+    jmethodID mid;
+    jobject context;
+    jobject manager;
 
     if (!LocalReferenceHolder_Init(&refs, env)) {
         LocalReferenceHolder_Cleanup(&refs);
         return NULL;
     }
 
-    jstring service = (*env)->NewStringUTF(env, name);
-
-    jmethodID mid;
+    service = (*env)->NewStringUTF(env, name);
 
     mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
-    jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+    context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
 
     mid = (*env)->GetMethodID(env, mActivityClass, "getSystemServiceFromUiThread", "(Ljava/lang/String;)Ljava/lang/Object;");
-    jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
+    manager = (*env)->CallObjectMethod(env, context, mid, service);
 
     (*env)->DeleteLocalRef(env, service);
 
@@ -1117,11 +1229,12 @@ static jobject Android_JNI_GetSystemServiceObject(const char* name)
 #define SETUP_CLIPBOARD(error) \
     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
     JNIEnv* env = Android_JNI_GetEnv(); \
+    jobject clipboard; \
     if (!LocalReferenceHolder_Init(&refs, env)) { \
         LocalReferenceHolder_Cleanup(&refs); \
         return error; \
     } \
-    jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
+    clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
     if (!clipboard) { \
         LocalReferenceHolder_Cleanup(&refs); \
         return error; \
@@ -1132,14 +1245,17 @@ static jobject Android_JNI_GetSystemServiceObject(const char* name)
 
 int Android_JNI_SetClipboardText(const char* text)
 {
+    /* Watch out for C89 scoping rules because of the macro */
     SETUP_CLIPBOARD(-1)
 
-    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
-    jstring string = (*env)->NewStringUTF(env, text);
-    (*env)->CallVoidMethod(env, clipboard, mid, string);
-    (*env)->DeleteGlobalRef(env, clipboard);
-    (*env)->DeleteLocalRef(env, string);
-
+    /* Nest the following in a scope to avoid C89 declaration rules triggered by the macro */
+    {
+        jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
+        jstring string = (*env)->NewStringUTF(env, text);
+        (*env)->CallVoidMethod(env, clipboard, mid, string);
+        (*env)->DeleteGlobalRef(env, clipboard);
+        (*env)->DeleteLocalRef(env, string);
+    }
     CLEANUP_CLIPBOARD();
 
     return 0;
@@ -1147,25 +1263,30 @@ int Android_JNI_SetClipboardText(const char* text)
 
 char* Android_JNI_GetClipboardText(void)
 {
+    /* Watch out for C89 scoping rules because of the macro */
     SETUP_CLIPBOARD(SDL_strdup(""))
 
-    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
-    jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
-    (*env)->DeleteGlobalRef(env, clipboard);
-    if (sequence) {
-        mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
-        jstring string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
-        const char* utf = (*env)->GetStringUTFChars(env, string, 0);
-        if (utf) {
-            char* text = SDL_strdup(utf);
-            (*env)->ReleaseStringUTFChars(env, string, utf);
-
-            CLEANUP_CLIPBOARD();
-
-            return text;
+    /* Nest the following in a scope to avoid C89 declaration rules triggered by the macro */
+    {
+        jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
+        jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
+        (*env)->DeleteGlobalRef(env, clipboard);
+        if (sequence) {
+            jstring string;
+            const char* utf;
+            mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
+            string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
+            utf = (*env)->GetStringUTFChars(env, string, 0);
+            if (utf) {
+                char* text = SDL_strdup(utf);
+                (*env)->ReleaseStringUTFChars(env, string, utf);
+
+                CLEANUP_CLIPBOARD();
+
+                return text;
+            }
         }
     }
-
     CLEANUP_CLIPBOARD();    
 
     return SDL_strdup("");
@@ -1173,10 +1294,13 @@ char* Android_JNI_GetClipboardText(void)
 
 SDL_bool Android_JNI_HasClipboardText(void)
 {
+    jmethodID mid;
+    jboolean has;
+    /* Watch out for C89 scoping rules because of the macro */
     SETUP_CLIPBOARD(SDL_FALSE)
 
-    jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
-    jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
+    mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
+    has = (*env)->CallBooleanMethod(env, clipboard, mid);
     (*env)->DeleteGlobalRef(env, clipboard);
 
     CLEANUP_CLIPBOARD();
@@ -1193,49 +1317,61 @@ int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seco
 {
     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
     JNIEnv* env = Android_JNI_GetEnv();
+    jmethodID mid;
+    jobject context;
+    jstring action;
+    jclass cls;
+    jobject filter;
+    jobject intent;
+    jstring iname;
+    jmethodID imid;
+    jstring bname;
+    jmethodID bmid;
     if (!LocalReferenceHolder_Init(&refs, env)) {
         LocalReferenceHolder_Cleanup(&refs);
         return -1;
     }
 
-    jmethodID mid;
 
     mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
-    jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
+    context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
 
-    jstring action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
+    action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
 
-    jclass cls = (*env)->FindClass(env, "android/content/IntentFilter");
+    cls = (*env)->FindClass(env, "android/content/IntentFilter");
 
     mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
-    jobject filter = (*env)->NewObject(env, cls, mid, action);
+    filter = (*env)->NewObject(env, cls, mid, action);
 
     (*env)->DeleteLocalRef(env, action);
 
     mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
-    jobject intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
+    intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
 
     (*env)->DeleteLocalRef(env, filter);
 
     cls = (*env)->GetObjectClass(env, intent);
 
-    jstring iname;
-    jmethodID imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
+    imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
 
+    /* Watch out for C89 scoping rules because of the macro */
 #define GET_INT_EXTRA(var, key) \
+    int var; \
     iname = (*env)->NewStringUTF(env, key); \
-    int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
+    var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
     (*env)->DeleteLocalRef(env, iname);
 
-    jstring bname;
-    jmethodID bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
+    bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
 
+    /* Watch out for C89 scoping rules because of the macro */
 #define GET_BOOL_EXTRA(var, key) \
+    int var; \
     bname = (*env)->NewStringUTF(env, key); \
-    int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
+    var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
     (*env)->DeleteLocalRef(env, bname);
 
     if (plugged) {
+        /* Watch out for C89 scoping rules because of the macro */
         GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
         if (plug == -1) {
             LocalReferenceHolder_Cleanup(&refs);
@@ -1247,6 +1383,7 @@ int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seco
     }
 
     if (charged) {
+        /* Watch out for C89 scoping rules because of the macro */
         GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
         if (status == -1) {
             LocalReferenceHolder_Cleanup(&refs);
@@ -1266,8 +1403,20 @@ int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seco
     }
 
     if (percent) {
-        GET_INT_EXTRA(level, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
-        GET_INT_EXTRA(scale, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
+        int level;
+        int scale;
+        
+        /* Watch out for C89 scoping rules because of the macro */
+        {
+            GET_INT_EXTRA(level_temp, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
+            level = level_temp;
+        }
+        /* Watch out for C89 scoping rules because of the macro */
+        {
+            GET_INT_EXTRA(scale_temp, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
+            scale = scale_temp;
+        }
+        
         if ((level == -1) || (scale == -1)) {
             LocalReferenceHolder_Cleanup(&refs);
             return -1;
@@ -1320,14 +1469,16 @@ void Android_JNI_PollInputDevices(void)
 int Android_JNI_SendMessage(int command, int param)
 {
     JNIEnv *env = Android_JNI_GetEnv();
+    jmethodID mid;
+    jboolean success;
     if (!env) {
         return -1;
     }
-    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
+    mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
     if (!mid) {
         return -1;
     }
-    jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
+    success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
     return success ? 0 : -1;
 }
 
@@ -1339,11 +1490,12 @@ void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
 {
     JNIEnv *env = Android_JNI_GetEnv();
+    jmethodID mid;
     if (!env) {
         return;
     }
 
-    jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
+    mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
     if (!mid) {
         return;
     }

+ 4 - 2
Engine/lib/sdl/src/core/android/SDL_android.h

@@ -40,10 +40,12 @@ extern void Android_JNI_HideTextInput(void);
 extern ANativeWindow* Android_JNI_GetNativeWindow(void);
 
 /* Audio support */
-extern int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames);
+extern int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames);
 extern void* Android_JNI_GetAudioBuffer(void);
 extern void Android_JNI_WriteAudioBuffer(void);
-extern void Android_JNI_CloseAudioDevice(void);
+extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
+extern void Android_JNI_FlushCapturedAudio(void);
+extern void Android_JNI_CloseAudioDevice(const int iscapture);
 
 #include "SDL_rwops.h"
 

+ 1 - 1
Engine/lib/sdl/src/core/linux/SDL_dbus.c

@@ -27,7 +27,7 @@
 static const char *dbus_library = "libdbus-1.so.3";
 static void *dbus_handle = NULL;
 static unsigned int screensaver_cookie = 0;
-static SDL_DBusContext dbus = {0};
+static SDL_DBusContext dbus;
 
 static int
 LoadDBUSSyms(void)

File diff suppressed because it is too large
+ 491 - 435
Engine/lib/sdl/src/core/linux/SDL_evdev.c


+ 0 - 20
Engine/lib/sdl/src/core/linux/SDL_evdev.h

@@ -27,31 +27,11 @@
 #ifdef SDL_INPUT_LINUXEV
 
 #include "SDL_events.h"
-#include <sys/stat.h>
-
-typedef struct SDL_evdevlist_item
-{
-    char *path;
-    int fd;
-    struct SDL_evdevlist_item *next;
-} SDL_evdevlist_item;
-
-typedef struct SDL_EVDEV_PrivateData
-{
-    SDL_evdevlist_item *first;
-    SDL_evdevlist_item *last;
-    int numdevices;
-    int ref_count;
-    int console_fd;
-    int kb_mode;
-    int tty;
-} SDL_EVDEV_PrivateData;
 
 extern int SDL_EVDEV_Init(void);
 extern void SDL_EVDEV_Quit(void);
 extern void SDL_EVDEV_Poll(void);
 
-
 #endif /* SDL_INPUT_LINUXEV */
 
 #endif /* _SDL_evdev_h */

+ 553 - 0
Engine/lib/sdl/src/core/linux/SDL_fcitx.c

@@ -0,0 +1,553 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 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.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef HAVE_FCITX_FRONTEND_H
+
+#include <fcitx/frontend.h>
+#include <unistd.h>
+
+#include "SDL_fcitx.h"
+#include "SDL_keycode.h"
+#include "SDL_keyboard.h"
+#include "../../events/SDL_keyboard_c.h"
+#include "SDL_dbus.h"
+#include "SDL_syswm.h"
+#if SDL_VIDEO_DRIVER_X11
+#  include "../../video/x11/SDL_x11video.h"
+#endif
+#include "SDL_hints.h"
+
+#define FCITX_DBUS_SERVICE "org.fcitx.Fcitx"
+
+#define FCITX_IM_DBUS_PATH "/inputmethod"
+#define FCITX_IC_DBUS_PATH "/inputcontext_%d"
+
+#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod"
+#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext"
+
+#define IC_NAME_MAX 64
+#define DBUS_TIMEOUT 500
+
+typedef struct _FcitxClient
+{
+    SDL_DBusContext *dbus;
+
+    char servicename[IC_NAME_MAX];
+    char icname[IC_NAME_MAX];
+
+    int id;
+
+    SDL_Rect cursor_rect;
+} FcitxClient;
+
+static FcitxClient fcitx_client;
+
+static int
+GetDisplayNumber()
+{
+    const char *display = SDL_getenv("DISPLAY");
+    const char *p = NULL;
+    int number = 0;
+
+    if (display == NULL)
+        return 0;
+
+    display = SDL_strchr(display, ':');
+    if (display == NULL)
+        return 0;
+
+    display++;
+    p = SDL_strchr(display, '.');
+    if (p == NULL && display != NULL) {
+        number = SDL_strtod(display, NULL);
+    } else {
+        char *buffer = SDL_strdup(display);
+        buffer[p - display] = '\0';
+        number = SDL_strtod(buffer, NULL);
+        SDL_free(buffer);
+    }
+
+    return number;
+}
+
+static char*
+GetAppName()
+{
+#if defined(__LINUX__) || defined(__FREEBSD__)
+    char *spot;
+    char procfile[1024];
+    char linkfile[1024];
+    int linksize;
+
+#if defined(__LINUX__)
+    SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
+#elif defined(__FREEBSD__)
+    SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());
+#endif
+    linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
+    if (linksize > 0) {
+        linkfile[linksize] = '\0';
+        spot = SDL_strrchr(linkfile, '/');
+        if (spot) {
+            return SDL_strdup(spot + 1);
+        } else {
+            return SDL_strdup(linkfile);
+        }
+    }
+#endif /* __LINUX__ || __FREEBSD__ */
+
+    return SDL_strdup("SDL_App");
+}
+
+/*
+ * Copied from fcitx source
+ */
+#define CONT(i)   ISUTF8_CB(in[i])
+#define VAL(i, s) ((in[i]&0x3f) << s)
+
+static char *
+_fcitx_utf8_get_char(const char *i, uint32_t *chr)
+{
+    const unsigned char* in = (const unsigned char *)i;
+    if (!(in[0] & 0x80)) {
+        *(chr) = *(in);
+        return (char *)in + 1;
+    }
+
+    /* 2-byte, 0x80-0x7ff */
+    if ((in[0] & 0xe0) == 0xc0 && CONT(1)) {
+        *chr = ((in[0] & 0x1f) << 6) | VAL(1, 0);
+        return (char *)in + 2;
+    }
+
+    /* 3-byte, 0x800-0xffff */
+    if ((in[0] & 0xf0) == 0xe0 && CONT(1) && CONT(2)) {
+        *chr = ((in[0] & 0xf) << 12) | VAL(1, 6) | VAL(2, 0);
+        return (char *)in + 3;
+    }
+
+    /* 4-byte, 0x10000-0x1FFFFF */
+    if ((in[0] & 0xf8) == 0xf0 && CONT(1) && CONT(2) && CONT(3)) {
+        *chr = ((in[0] & 0x7) << 18) | VAL(1, 12) | VAL(2, 6) | VAL(3, 0);
+        return (char *)in + 4;
+    }
+
+    /* 5-byte, 0x200000-0x3FFFFFF */
+    if ((in[0] & 0xfc) == 0xf8 && CONT(1) && CONT(2) && CONT(3) && CONT(4)) {
+        *chr = ((in[0] & 0x3) << 24) | VAL(1, 18) | VAL(2, 12) | VAL(3, 6) | VAL(4, 0);
+        return (char *)in + 5;
+    }
+
+    /* 6-byte, 0x400000-0x7FFFFFF */
+    if ((in[0] & 0xfe) == 0xfc && CONT(1) && CONT(2) && CONT(3) && CONT(4) && CONT(5)) {
+        *chr = ((in[0] & 0x1) << 30) | VAL(1, 24) | VAL(2, 18) | VAL(3, 12) | VAL(4, 6) | VAL(5, 0);
+        return (char *)in + 6;
+    }
+
+    *chr = *in;
+
+    return (char *)in + 1;
+}
+
+static size_t
+_fcitx_utf8_strlen(const char *s)
+{
+    unsigned int l = 0;
+
+    while (*s) {
+        uint32_t chr;
+
+        s = _fcitx_utf8_get_char(s, &chr);
+        l++;
+    }
+
+    return l;
+}
+
+static DBusHandlerResult
+DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+    SDL_DBusContext *dbus = (SDL_DBusContext *)data;
+
+    if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
+        DBusMessageIter iter;
+        const char *text = NULL;
+
+        dbus->message_iter_init(msg, &iter);
+        dbus->message_iter_get_basic(&iter, &text);
+
+        if (text)
+            SDL_SendKeyboardText(text);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdatePreedit")) {
+        DBusMessageIter iter;
+        const char *text;
+
+        dbus->message_iter_init(msg, &iter);
+        dbus->message_iter_get_basic(&iter, &text);
+
+        if (text && *text) {
+            char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
+            size_t text_bytes = SDL_strlen(text), i = 0;
+            size_t cursor = 0;
+
+            while (i < text_bytes) {
+                size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
+                size_t chars = _fcitx_utf8_strlen(buf);
+
+                SDL_SendEditingText(buf, cursor, chars);
+
+                i += sz;
+                cursor += chars;
+            }
+        }
+
+        SDL_Fcitx_UpdateTextRect(NULL);
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusMessage*
+FcitxClientICNewMethod(FcitxClient *client,
+        const char *method)
+{
+    SDL_DBusContext *dbus = client->dbus;
+    return dbus->message_new_method_call(
+            client->servicename,
+            client->icname,
+            FCITX_IC_DBUS_INTERFACE,
+            method);
+}
+
+static void
+FcitxClientICCallMethod(FcitxClient *client,
+        const char *method)
+{
+    SDL_DBusContext *dbus = client->dbus;
+    DBusMessage *msg = FcitxClientICNewMethod(client, method);
+
+    if (msg == NULL)
+        return ;
+
+    if (dbus->connection_send(dbus->session_conn, msg, NULL)) {
+        dbus->connection_flush(dbus->session_conn);
+    }
+
+    dbus->message_unref(msg);
+}
+
+static void
+Fcitx_SetCapabilities(void *data,
+        const char *name,
+        const char *old_val,
+        const char *internal_editing)
+{
+    FcitxClient *client = (FcitxClient *)data;
+    SDL_DBusContext *dbus = client->dbus;
+    Uint32 caps = CAPACITY_NONE;
+
+    DBusMessage *msg = FcitxClientICNewMethod(client, "SetCapacity");
+    if (msg == NULL)
+        return ;
+
+    if (!(internal_editing && *internal_editing == '1')) {
+        caps |= CAPACITY_PREEDIT;
+    }
+
+    dbus->message_append_args(msg,
+            DBUS_TYPE_UINT32, &caps,
+            DBUS_TYPE_INVALID);
+    if (dbus->connection_send(dbus->session_conn, msg, NULL)) {
+        dbus->connection_flush(dbus->session_conn);
+    }
+
+    dbus->message_unref(msg);
+}
+
+static void
+FcitxClientCreateIC(FcitxClient *client)
+{
+    char *appname = NULL;
+    pid_t pid = 0;
+    int id = 0;
+    SDL_bool enable;
+    Uint32 arg1, arg2, arg3, arg4;
+
+    SDL_DBusContext *dbus = client->dbus;
+    DBusMessage *reply = NULL;
+    DBusMessage *msg = dbus->message_new_method_call(
+            client->servicename,
+            FCITX_IM_DBUS_PATH,
+            FCITX_IM_DBUS_INTERFACE,
+            "CreateICv3"
+            );
+
+    if (msg == NULL)
+        return ;
+
+    appname = GetAppName();
+    pid = getpid();
+    dbus->message_append_args(msg,
+            DBUS_TYPE_STRING, &appname,
+            DBUS_TYPE_INT32, &pid,
+            DBUS_TYPE_INVALID);
+
+    do {
+        reply = dbus->connection_send_with_reply_and_block(
+                dbus->session_conn,
+                msg,
+                DBUS_TIMEOUT,
+                NULL);
+
+        if (!reply)
+            break;
+        if (!dbus->message_get_args(reply, NULL,
+                DBUS_TYPE_INT32, &id,
+                DBUS_TYPE_BOOLEAN, &enable,
+                DBUS_TYPE_UINT32, &arg1,
+                DBUS_TYPE_UINT32, &arg2,
+                DBUS_TYPE_UINT32, &arg3,
+                DBUS_TYPE_UINT32, &arg4,
+                DBUS_TYPE_INVALID))
+            break;
+
+        if (id < 0)
+            break;
+        client->id = id;
+
+        SDL_snprintf(client->icname, IC_NAME_MAX,
+                FCITX_IC_DBUS_PATH, client->id);
+
+        dbus->bus_add_match(dbus->session_conn,
+                "type='signal', interface='org.fcitx.Fcitx.InputContext'",
+                NULL);
+        dbus->connection_add_filter(dbus->session_conn,
+                &DBus_MessageFilter, dbus,
+                NULL);
+        dbus->connection_flush(dbus->session_conn);
+
+        SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, &Fcitx_SetCapabilities, client);
+    }
+    while (0);
+
+    if (reply)
+        dbus->message_unref(reply);
+    dbus->message_unref(msg);
+    SDL_free(appname);
+}
+
+static Uint32
+Fcitx_ModState(void)
+{
+    Uint32 fcitx_mods = 0;
+    SDL_Keymod sdl_mods = SDL_GetModState();
+
+    if (sdl_mods & KMOD_SHIFT) fcitx_mods |= FcitxKeyState_Shift;
+    if (sdl_mods & KMOD_CAPS)   fcitx_mods |= FcitxKeyState_CapsLock;
+    if (sdl_mods & KMOD_CTRL)  fcitx_mods |= FcitxKeyState_Ctrl;
+    if (sdl_mods & KMOD_ALT)   fcitx_mods |= FcitxKeyState_Alt;
+    if (sdl_mods & KMOD_NUM)    fcitx_mods |= FcitxKeyState_NumLock;
+    if (sdl_mods & KMOD_LGUI)   fcitx_mods |= FcitxKeyState_Super;
+    if (sdl_mods & KMOD_RGUI)   fcitx_mods |= FcitxKeyState_Meta;
+
+    return fcitx_mods;
+}
+
+SDL_bool
+SDL_Fcitx_Init()
+{
+    fcitx_client.dbus = SDL_DBus_GetContext();
+
+    fcitx_client.cursor_rect.x = -1;
+    fcitx_client.cursor_rect.y = -1;
+    fcitx_client.cursor_rect.w = 0;
+    fcitx_client.cursor_rect.h = 0;
+
+    SDL_snprintf(fcitx_client.servicename, IC_NAME_MAX,
+            "%s-%d",
+            FCITX_DBUS_SERVICE, GetDisplayNumber());
+
+    FcitxClientCreateIC(&fcitx_client);
+
+    return SDL_TRUE;
+}
+
+void
+SDL_Fcitx_Quit()
+{
+    FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
+}
+
+void
+SDL_Fcitx_SetFocus(SDL_bool focused)
+{
+    if (focused) {
+        FcitxClientICCallMethod(&fcitx_client, "FocusIn");
+    } else {
+        FcitxClientICCallMethod(&fcitx_client, "FocusOut");
+    }
+}
+
+void
+SDL_Fcitx_Reset(void)
+{
+    FcitxClientICCallMethod(&fcitx_client, "Reset");
+    FcitxClientICCallMethod(&fcitx_client, "CloseIC");
+}
+
+SDL_bool
+SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
+{
+    DBusMessage *msg = NULL;
+    DBusMessage *reply = NULL;
+    SDL_DBusContext *dbus = fcitx_client.dbus;
+
+    Uint32 state = 0;
+    SDL_bool handled = SDL_FALSE;
+    int type = FCITX_PRESS_KEY;
+    Uint32 event_time = 0;
+
+    msg = FcitxClientICNewMethod(&fcitx_client, "ProcessKeyEvent");
+    if (msg == NULL)
+        return SDL_FALSE;
+
+    state = Fcitx_ModState();
+    dbus->message_append_args(msg,
+            DBUS_TYPE_UINT32, &keysym,
+            DBUS_TYPE_UINT32, &keycode,
+            DBUS_TYPE_UINT32, &state,
+            DBUS_TYPE_INT32, &type,
+            DBUS_TYPE_UINT32, &event_time,
+            DBUS_TYPE_INVALID);
+
+    reply = dbus->connection_send_with_reply_and_block(dbus->session_conn,
+            msg,
+            -1,
+            NULL);
+
+    if (reply) {
+        dbus->message_get_args(reply,
+                NULL,
+                DBUS_TYPE_INT32, &handled,
+                DBUS_TYPE_INVALID);
+
+        dbus->message_unref(reply);
+    }
+
+    if (handled) {
+        SDL_Fcitx_UpdateTextRect(NULL);
+    }
+
+    return handled;
+}
+
+void
+SDL_Fcitx_UpdateTextRect(SDL_Rect *rect)
+{
+    SDL_Window *focused_win = NULL;
+    SDL_SysWMinfo info;
+    int x = 0, y = 0;
+    SDL_Rect *cursor = &fcitx_client.cursor_rect;
+
+    SDL_DBusContext *dbus = fcitx_client.dbus;
+    DBusMessage *msg = NULL;
+    DBusConnection *conn;
+
+    if (rect) {
+        SDL_memcpy(cursor, rect, sizeof(SDL_Rect));
+    }
+
+    focused_win = SDL_GetKeyboardFocus();
+    if (!focused_win) {
+        return ;
+    }
+
+    SDL_VERSION(&info.version);
+    if (!SDL_GetWindowWMInfo(focused_win, &info)) {
+        return;
+    }
+
+    SDL_GetWindowPosition(focused_win, &x, &y);
+
+#if SDL_VIDEO_DRIVER_X11
+    if (info.subsystem == SDL_SYSWM_X11) {
+        SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata;
+
+        Display *x_disp = info.info.x11.display;
+        Window x_win = info.info.x11.window;
+        int x_screen = displaydata->screen;
+        Window unused;
+        X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
+    }
+#endif
+
+    if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
+        // move to bottom left
+        int w = 0, h = 0;
+        SDL_GetWindowSize(focused_win, &w, &h);
+        cursor->x = 0;
+        cursor->y = h;
+    }
+
+    x += cursor->x;
+    y += cursor->y;
+
+    msg = FcitxClientICNewMethod(&fcitx_client, "SetCursorRect");
+    if (msg == NULL)
+        return ;
+
+    dbus->message_append_args(msg,
+            DBUS_TYPE_INT32, &x,
+            DBUS_TYPE_INT32, &y,
+            DBUS_TYPE_INT32, &cursor->w,
+            DBUS_TYPE_INT32, &cursor->h,
+            DBUS_TYPE_INVALID);
+
+    conn = dbus->session_conn;
+    if (dbus->connection_send(conn, msg, NULL))
+        dbus->connection_flush(conn);
+
+    dbus->message_unref(msg);
+}
+
+void
+SDL_Fcitx_PumpEvents()
+{
+    SDL_DBusContext *dbus = fcitx_client.dbus;
+    DBusConnection *conn = dbus->session_conn;
+
+    dbus->connection_read_write(conn, 0);
+
+    while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
+        /* Do nothing, actual work happens in DBus_MessageFilter */
+        usleep(10);
+    }
+}
+
+#endif /* HAVE_FCITX_FRONTEND_H */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 40 - 0
Engine/lib/sdl/src/core/linux/SDL_fcitx.h

@@ -0,0 +1,40 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 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_fcitx_h
+#define _SDL_fcitx_h
+
+#include "../../SDL_internal.h"
+
+#include "SDL_stdinc.h"
+#include "SDL_rect.h"
+
+extern SDL_bool SDL_Fcitx_Init(void);
+extern void SDL_Fcitx_Quit(void);
+extern void SDL_Fcitx_SetFocus(SDL_bool focused);
+extern void SDL_Fcitx_Reset(void);
+extern SDL_bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode);
+extern void SDL_Fcitx_UpdateTextRect(SDL_Rect *rect);
+extern void SDL_Fcitx_PumpEvents();
+
+#endif /* _SDL_fcitx_h */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 4 - 2
Engine/lib/sdl/src/core/linux/SDL_ibus.c

@@ -42,7 +42,7 @@ static const char IBUS_INTERFACE[]       = "org.freedesktop.IBus";
 static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
 
 static char *input_ctx_path = NULL;
-static SDL_Rect ibus_cursor_rect = {0};
+static SDL_Rect ibus_cursor_rect = { 0, 0, 0, 0 };
 static DBusConnection *ibus_conn = NULL;
 static char *ibus_addr_file = NULL;
 int inotify_fd = -1, inotify_wd = -1;
@@ -341,7 +341,9 @@ IBus_SetupConnection(SDL_DBusContext *dbus, const char* addr)
     const char *path = NULL;
     SDL_bool result = SDL_FALSE;
     DBusMessage *msg;
-    DBusObjectPathVTable ibus_vtable = {0};
+    DBusObjectPathVTable ibus_vtable;
+
+    SDL_zero(ibus_vtable);
     ibus_vtable.message_function = &IBus_MessageHandler;
 
     ibus_conn = dbus->connection_open_private(addr, NULL);

+ 138 - 0
Engine/lib/sdl/src/core/linux/SDL_ime.c

@@ -0,0 +1,138 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 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.
+*/
+
+#include "SDL_ime.h"
+#include "SDL_ibus.h"
+#include "SDL_fcitx.h"
+
+typedef SDL_bool (*_SDL_IME_Init)();
+typedef void (*_SDL_IME_Quit)();
+typedef void (*_SDL_IME_SetFocus)(SDL_bool);
+typedef void (*_SDL_IME_Reset)();
+typedef SDL_bool (*_SDL_IME_ProcessKeyEvent)(Uint32, Uint32);
+typedef void (*_SDL_IME_UpdateTextRect)(SDL_Rect *);
+typedef void (*_SDL_IME_PumpEvents)();
+
+static _SDL_IME_Init SDL_IME_Init_Real = NULL;
+static _SDL_IME_Quit SDL_IME_Quit_Real = NULL;
+static _SDL_IME_SetFocus SDL_IME_SetFocus_Real = NULL;
+static _SDL_IME_Reset SDL_IME_Reset_Real = NULL;
+static _SDL_IME_ProcessKeyEvent SDL_IME_ProcessKeyEvent_Real = NULL;
+static _SDL_IME_UpdateTextRect SDL_IME_UpdateTextRect_Real = NULL;
+static _SDL_IME_PumpEvents SDL_IME_PumpEvents_Real = NULL;
+
+static void
+InitIME()
+{
+    static SDL_bool inited = SDL_FALSE;
+#ifdef HAVE_FCITX_FRONTEND_H
+    const char *im_module = SDL_getenv("SDL_IM_MODULE");
+    const char *xmodifiers = SDL_getenv("XMODIFIERS");
+#endif
+
+    if (inited == SDL_TRUE)
+        return;
+
+    inited = SDL_TRUE;
+
+    /* See if fcitx IME support is being requested */
+#ifdef HAVE_FCITX_FRONTEND_H
+    if (!SDL_IME_Init_Real &&
+        ((im_module && SDL_strcmp(im_module, "fcitx") == 0) ||
+         (!im_module && xmodifiers && SDL_strstr(xmodifiers, "@im=fcitx") != NULL))) {
+        SDL_IME_Init_Real = SDL_Fcitx_Init;
+        SDL_IME_Quit_Real = SDL_Fcitx_Quit;
+        SDL_IME_SetFocus_Real = SDL_Fcitx_SetFocus;
+        SDL_IME_Reset_Real = SDL_Fcitx_Reset;
+        SDL_IME_ProcessKeyEvent_Real = SDL_Fcitx_ProcessKeyEvent;
+        SDL_IME_UpdateTextRect_Real = SDL_Fcitx_UpdateTextRect;
+        SDL_IME_PumpEvents_Real = SDL_Fcitx_PumpEvents;
+    }
+#endif /* HAVE_FCITX_FRONTEND_H */
+
+    /* default to IBus */
+#ifdef HAVE_IBUS_IBUS_H
+    if (!SDL_IME_Init_Real) {
+        SDL_IME_Init_Real = SDL_IBus_Init;
+        SDL_IME_Quit_Real = SDL_IBus_Quit;
+        SDL_IME_SetFocus_Real = SDL_IBus_SetFocus;
+        SDL_IME_Reset_Real = SDL_IBus_Reset;
+        SDL_IME_ProcessKeyEvent_Real = SDL_IBus_ProcessKeyEvent;
+        SDL_IME_UpdateTextRect_Real = SDL_IBus_UpdateTextRect;
+        SDL_IME_PumpEvents_Real = SDL_IBus_PumpEvents;
+    }
+#endif /* HAVE_IBUS_IBUS_H */
+}
+
+SDL_bool
+SDL_IME_Init(void)
+{
+    InitIME();
+
+    if (SDL_IME_Init_Real)
+        return SDL_IME_Init_Real();
+
+    return SDL_FALSE;
+}
+
+void
+SDL_IME_Quit(void)
+{
+    if (SDL_IME_Quit_Real)
+        SDL_IME_Quit_Real();
+}
+
+void
+SDL_IME_SetFocus(SDL_bool focused)
+{
+    if (SDL_IME_SetFocus_Real)
+        SDL_IME_SetFocus_Real(focused);
+}
+
+void
+SDL_IME_Reset(void)
+{
+    if (SDL_IME_Reset_Real)
+        SDL_IME_Reset_Real();
+}
+
+SDL_bool
+SDL_IME_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
+{
+    if (SDL_IME_ProcessKeyEvent_Real)
+        return SDL_IME_ProcessKeyEvent_Real(keysym, keycode);
+
+    return SDL_FALSE;
+}
+
+void
+SDL_IME_UpdateTextRect(SDL_Rect *rect)
+{
+    if (SDL_IME_UpdateTextRect_Real)
+        SDL_IME_UpdateTextRect_Real(rect);
+}
+
+void
+SDL_IME_PumpEvents()
+{
+    if (SDL_IME_PumpEvents_Real)
+        SDL_IME_PumpEvents_Real();
+}

+ 38 - 0
Engine/lib/sdl/src/core/linux/SDL_ime.h

@@ -0,0 +1,38 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2016 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_ime_h
+#define _SDL_ime_h
+
+#include "../../SDL_internal.h"
+
+#include "SDL_stdinc.h"
+#include "SDL_rect.h"
+
+extern SDL_bool SDL_IME_Init();
+extern void SDL_IME_Quit();
+extern void SDL_IME_SetFocus(SDL_bool focused);
+extern void SDL_IME_Reset();
+extern SDL_bool SDL_IME_ProcessKeyEvent(Uint32 keysym, Uint32 keycode);
+extern void SDL_IME_UpdateTextRect(SDL_Rect *rect);
+extern void SDL_IME_PumpEvents();
+
+#endif /* _SDL_ime_h */

+ 8 - 1
Engine/lib/sdl/src/core/linux/SDL_udev.c

@@ -349,7 +349,9 @@ guess_device_class(struct udev_device *dev)
         } else if (test_bit(BTN_MOUSE, bitmask_key)) {
             devclass |= SDL_UDEV_DEVICE_MOUSE; /* ID_INPUT_MOUSE */
         } else if (test_bit(BTN_TOUCH, bitmask_key)) {
-            ; /* ID_INPUT_TOUCHSCREEN */
+            /* TODO: better determining between touchscreen and multitouch touchpad,
+               see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
+            devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; /* ID_INPUT_TOUCHSCREEN */
         }
 
         if (test_bit(BTN_TRIGGER, bitmask_key) ||
@@ -411,6 +413,11 @@ device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
         if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
             devclass |= SDL_UDEV_DEVICE_MOUSE;
         }
+        
+        val = _this->udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
+        if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
+            devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
+        }
 
         /* The undocumented rule is:
            - All devices with keys get ID_INPUT_KEY

+ 4 - 2
Engine/lib/sdl/src/core/linux/SDL_udev.h

@@ -42,17 +42,19 @@
 
 typedef enum
 {
-    SDL_UDEV_DEVICEADDED = 0x0001,
+    SDL_UDEV_DEVICEADDED = 1,
     SDL_UDEV_DEVICEREMOVED
 } SDL_UDEV_deviceevent;
 
 /* A device can be any combination of these classes */
 typedef enum
 {
+    SDL_UDEV_DEVICE_UNKNOWN     = 0x0000,
     SDL_UDEV_DEVICE_MOUSE       = 0x0001,
     SDL_UDEV_DEVICE_KEYBOARD    = 0x0002,
     SDL_UDEV_DEVICE_JOYSTICK    = 0x0004,
-    SDL_UDEV_DEVICE_SOUND       = 0x0008
+    SDL_UDEV_DEVICE_SOUND       = 0x0008,
+    SDL_UDEV_DEVICE_TOUCHSCREEN = 0x0010
 } SDL_UDEV_deviceclass;
 
 typedef void (*SDL_UDEV_Callback)(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);

+ 78 - 0
Engine/lib/sdl/src/core/windows/SDL_windows.c

@@ -124,6 +124,84 @@ BOOL WIN_IsWindowsVistaOrGreater()
 #endif
 }
 
+/*
+WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's
+longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which
+will give you a name GUID. The full name is in the Windows Registry under
+that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories
+
+Note that drivers can report GUID_NULL for the name GUID, in which case,
+Windows makes a best effort to fill in those 31 bytes in the usual place.
+This info summarized from MSDN:
+
+http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx
+
+Always look this up in the registry if possible, because the strings are
+different! At least on Win10, I see "Yeti Stereo Microphone" in the
+Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh.
+
+(Also, DirectSound shouldn't be limited to 32 chars, but its device enum
+has the same problem.)
+*/
+char *
+WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid)
+{
+#if __WINRT__
+    return WIN_StringToUTF8(name);  /* No registry access on WinRT/UWP, go with what we've got. */
+#else
+    static const GUID nullguid = { 0 };
+    const unsigned char *ptr;
+    char keystr[128];
+    WCHAR *strw = NULL;
+    SDL_bool rc;
+    HKEY hkey;
+    DWORD len = 0;
+    char *retval = NULL;
+
+    if (SDL_memcmp(guid, &nullguid, sizeof (*guid)) == 0) {
+        return WIN_StringToUTF8(name);  /* No GUID, go with what we've got. */
+    }
+
+    ptr = (const unsigned char *) guid;
+    SDL_snprintf(keystr, sizeof (keystr),
+        "System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+        ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6],
+        ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
+
+    strw = WIN_UTF8ToString(keystr);
+    rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS);
+    SDL_free(strw);
+    if (!rc) {
+        return WIN_StringToUTF8(name);  /* oh well. */
+    }
+
+    rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS);
+    if (!rc) {
+        RegCloseKey(hkey);
+        return WIN_StringToUTF8(name);  /* oh well. */
+    }
+
+    strw = (WCHAR *) SDL_malloc(len + sizeof (WCHAR));
+    if (!strw) {
+        RegCloseKey(hkey);
+        return WIN_StringToUTF8(name);  /* oh well. */
+    }
+
+    rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE) strw, &len) == ERROR_SUCCESS);
+    RegCloseKey(hkey);
+    if (!rc) {
+        SDL_free(strw);
+        return WIN_StringToUTF8(name);  /* oh well. */
+    }
+
+    strw[len / 2] = 0;  /* make sure it's null-terminated. */
+
+    retval = WIN_StringToUTF8(strw);
+    SDL_free(strw);
+    return retval ? retval : WIN_StringToUTF8(name);
+#endif /* if __WINRT__ / else */
+}
+
 #endif /* __WIN32__ || __WINRT__ */
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 3 - 0
Engine/lib/sdl/src/core/windows/SDL_windows.h

@@ -59,6 +59,9 @@ extern void WIN_CoUninitialize(void);
 /* Returns SDL_TRUE if we're running on Windows Vista and newer */
 extern BOOL WIN_IsWindowsVistaOrGreater();
 
+/* You need to SDL_free() the result of this call. */
+extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);
+
 #endif /* _INCLUDED_WINDOWS_H */
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 26 - 5
Engine/lib/sdl/src/core/winrt/SDL_winrtapp_direct3d.cpp

@@ -254,6 +254,18 @@ void SDL_WinRTApp::Initialize(CoreApplicationView^ applicationView)
 
     CoreApplication::Exiting +=
         ref new EventHandler<Platform::Object^>(this, &SDL_WinRTApp::OnExiting);
+
+#if NTDDI_VERSION >= NTDDI_WIN10
+    /* HACK ALERT!  Xbox One doesn't seem to detect gamepads unless something
+       gets registered to receive Win10's Windows.Gaming.Input.Gamepad.GamepadAdded
+       events.  We'll register an event handler for these events here, to make
+       sure that gamepad detection works later on, if requested.
+    */
+    Windows::Gaming::Input::Gamepad::GamepadAdded +=
+        ref new Windows::Foundation::EventHandler<Windows::Gaming::Input::Gamepad^>(
+            this, &SDL_WinRTApp::OnGamepadAdded
+        );
+#endif
 }
 
 #if NTDDI_VERSION > NTDDI_WIN8
@@ -810,11 +822,8 @@ static void WINRT_OnBackButtonPressed(BackButtonEventArgs ^ args)
     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_AC_BACK);
     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_AC_BACK);
 
-    const char *hint = SDL_GetHint(SDL_HINT_WINRT_HANDLE_BACK_BUTTON);
-    if (hint) {
-        if (*hint == '1') {
-            args->Handled = true;
-        }
+    if (SDL_GetHintBoolean(SDL_HINT_WINRT_HANDLE_BACK_BUTTON, SDL_FALSE)) {
+        args->Handled = true;
     }
 }
 
@@ -832,3 +841,15 @@ void SDL_WinRTApp::OnBackButtonPressed(Platform::Object^ sender, Windows::Phone:
 }
 #endif
 
+#if NTDDI_VERSION >= NTDDI_WIN10
+void SDL_WinRTApp::OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad)
+{
+    /* HACK ALERT: Nothing needs to be done here, as this method currently
+       only exists to allow something to be registered with Win10's
+       GamepadAdded event, an operation that seems to be necessary to get
+       Xinput-based detection to work on Xbox One.
+    */
+}
+#endif
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 4 - 0
Engine/lib/sdl/src/core/winrt/SDL_winrtapp_direct3d.h

@@ -80,6 +80,10 @@ protected:
     void OnBackButtonPressed(Platform::Object^ sender, Windows::Phone::UI::Input::BackPressedEventArgs^ args);
 #endif
 
+#if NTDDI_VERSION >= NTDDI_WIN10
+    void OnGamepadAdded(Platform::Object ^sender, Windows::Gaming::Input::Gamepad ^gamepad);
+#endif
+
 private:
     bool m_windowClosed;
     bool m_windowVisible;

+ 1 - 1
Engine/lib/sdl/src/dynapi/SDL_dynapi.c

@@ -293,7 +293,7 @@ SDL_InitDynamicAPI(void)
      *  SDL_CreateThread() would also call this function before building the
      *  new thread).
      */
-    static volatile SDL_bool already_initialized = SDL_FALSE;
+    static SDL_bool already_initialized = SDL_FALSE;
 
     /* SDL_AtomicLock calls SDL mutex functions to emulate if
        SDL_ATOMIC_DISABLED, which we can't do here, so in such a

+ 8 - 2
Engine/lib/sdl/src/dynapi/SDL_dynapi.h

@@ -43,9 +43,15 @@
 #include "TargetConditionals.h"
 #endif
 
-#if TARGET_OS_IPHONE || __native_client__ || __EMSCRIPTEN__  /* probably not useful on iOS, NACL or Emscripten. */
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE  /* probably not useful on iOS. */
 #define SDL_DYNAMIC_API 0
-#elif SDL_BUILDING_WINRT /* probaly not useful on WinRT, given current .dll loading restrictions */
+#elif defined(__native_client__) && __native_client__  /* probably not useful on NACL. */
+#define SDL_DYNAMIC_API 0
+#elif defined(__EMSCRIPTEN__) && __EMSCRIPTEN__  /* probably not useful on Emscripten. */
+#define SDL_DYNAMIC_API 0
+#elif defined(SDL_BUILDING_WINRT) && SDL_BUILDING_WINRT  /* probably not useful on WinRT, given current .dll loading restrictions */
+#define SDL_DYNAMIC_API 0
+#elif defined(__PSP__) && __PSP__
 #define SDL_DYNAMIC_API 0
 #elif defined(__clang_analyzer__)
 #define SDL_DYNAMIC_API 0  /* Turn off for static analysis, so reports are more clear. */

+ 15 - 0
Engine/lib/sdl/src/dynapi/SDL_dynapi_overrides.h

@@ -445,6 +445,8 @@
 #define SDL_iconv_close SDL_iconv_close_REAL
 #define SDL_iconv SDL_iconv_REAL
 #define SDL_iconv_string SDL_iconv_string_REAL
+#define SDL_CreateRGBSurfaceWithFormat SDL_CreateRGBSurfaceWithFormat_REAL
+#define SDL_CreateRGBSurfaceWithFormatFrom SDL_CreateRGBSurfaceWithFormatFrom_REAL
 #define SDL_CreateRGBSurface SDL_CreateRGBSurface_REAL
 #define SDL_CreateRGBSurfaceFrom SDL_CreateRGBSurfaceFrom_REAL
 #define SDL_FreeSurface SDL_FreeSurface_REAL
@@ -597,3 +599,16 @@
 #define SDL_JoystickCurrentPowerLevel SDL_JoystickCurrentPowerLevel_REAL
 #define SDL_GameControllerFromInstanceID SDL_GameControllerFromInstanceID_REAL
 #define SDL_JoystickFromInstanceID SDL_JoystickFromInstanceID_REAL
+#define SDL_GetDisplayUsableBounds SDL_GetDisplayUsableBounds_REAL
+#define SDL_GetWindowBordersSize SDL_GetWindowBordersSize_REAL
+#define SDL_SetWindowOpacity SDL_SetWindowOpacity_REAL
+#define SDL_GetWindowOpacity SDL_GetWindowOpacity_REAL
+#define SDL_SetWindowInputFocus SDL_SetWindowInputFocus_REAL
+#define SDL_SetWindowModalFor SDL_SetWindowModalFor_REAL
+#define SDL_RenderSetIntegerScale SDL_RenderSetIntegerScale_REAL
+#define SDL_RenderGetIntegerScale SDL_RenderGetIntegerScale_REAL
+#define SDL_DequeueAudio SDL_DequeueAudio_REAL
+#define SDL_SetWindowResizable SDL_SetWindowResizable_REAL
+#define SDL_CreateRGBSurfaceWithFormat SDL_CreateRGBSurfaceWithFormat_REAL
+#define SDL_CreateRGBSurfaceWithFormatFrom SDL_CreateRGBSurfaceWithFormatFrom_REAL
+#define SDL_GetHintBoolean SDL_GetHintBoolean_REAL

+ 13 - 0
Engine/lib/sdl/src/dynapi/SDL_dynapi_procs.h

@@ -631,3 +631,16 @@ SDL_DYNAPI_PROC(int,SDL_GetDisplayDPI,(int a, float *b, float *c, float *d),(a,b
 SDL_DYNAPI_PROC(SDL_JoystickPowerLevel,SDL_JoystickCurrentPowerLevel,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(SDL_GameController*,SDL_GameControllerFromInstanceID,(SDL_JoystickID a),(a),return)
 SDL_DYNAPI_PROC(SDL_Joystick*,SDL_JoystickFromInstanceID,(SDL_JoystickID a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GetDisplayUsableBounds,(int a, SDL_Rect *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_GetWindowBordersSize,(SDL_Window *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return)
+SDL_DYNAPI_PROC(int,SDL_SetWindowOpacity,(SDL_Window *a, float b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_GetWindowOpacity,(SDL_Window *a, float *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_SetWindowInputFocus,(SDL_Window *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_SetWindowModalFor,(SDL_Window *a, SDL_Window *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_RenderSetIntegerScale,(SDL_Renderer *a, SDL_bool b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_RenderGetIntegerScale,(SDL_Renderer *a),(a),return)
+SDL_DYNAPI_PROC(Uint32,SDL_DequeueAudio,(SDL_AudioDeviceID a, void *b, Uint32 c),(a,b,c),return)
+SDL_DYNAPI_PROC(void,SDL_SetWindowResizable,(SDL_Window *a, SDL_bool b),(a,b),)
+SDL_DYNAPI_PROC(SDL_Surface*,SDL_CreateRGBSurfaceWithFormat,(Uint32 a, int b, int c, int d, Uint32 e),(a,b,c,d,e),return)
+SDL_DYNAPI_PROC(SDL_Surface*,SDL_CreateRGBSurfaceWithFormatFrom,(void *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_GetHintBoolean,(const char *a, SDL_bool b),(a,b),return)

+ 60 - 8
Engine/lib/sdl/src/events/SDL_dropevents.c

@@ -26,21 +26,73 @@
 #include "SDL_events_c.h"
 #include "SDL_dropevents_c.h"
 
+#include "../video/SDL_sysvideo.h"  /* for SDL_Window internals. */
 
-int
-SDL_SendDropFile(const char *file)
+
+static int
+SDL_SendDrop(SDL_Window *window, const SDL_EventType evtype, const char *data)
 {
-    int posted;
+    static SDL_bool app_is_dropping = SDL_FALSE;
+    int posted = 0;
 
     /* Post the event, if desired */
-    posted = 0;
-    if (SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) {
+    if (SDL_GetEventState(evtype) == SDL_ENABLE) {
+        const SDL_bool need_begin = window ? !window->is_dropping : !app_is_dropping;
         SDL_Event event;
-        event.type = SDL_DROPFILE;
-        event.drop.file = SDL_strdup(file);
+
+        if (need_begin) {
+            SDL_zero(event);
+            event.type = SDL_DROPBEGIN;
+
+            if (window) {
+                event.drop.windowID = window->id;
+            }
+
+            posted = (SDL_PushEvent(&event) > 0);
+            if (!posted) {
+                return 0;
+            }
+            if (window) {
+                window->is_dropping = SDL_TRUE;
+            } else {
+                app_is_dropping = SDL_TRUE;
+            }
+        }
+
+        SDL_zero(event);
+        event.type = evtype;
+        event.drop.file = data ? SDL_strdup(data) : NULL;
+        event.drop.windowID = window ? window->id : 0;
         posted = (SDL_PushEvent(&event) > 0);
+
+        if (posted && (evtype == SDL_DROPCOMPLETE)) {
+            if (window) {
+                window->is_dropping = SDL_FALSE;
+            } else {
+                app_is_dropping = SDL_FALSE;
+            }
+        }
     }
-    return (posted);
+    return posted;
+}
+
+int
+SDL_SendDropFile(SDL_Window *window, const char *file)
+{
+    return SDL_SendDrop(window, SDL_DROPFILE, file);
+}
+
+int
+SDL_SendDropText(SDL_Window *window, const char *text)
+{
+    return SDL_SendDrop(window, SDL_DROPTEXT, text);
 }
 
+int
+SDL_SendDropComplete(SDL_Window *window)
+{
+    return SDL_SendDrop(window, SDL_DROPCOMPLETE, NULL);
+}
+
+
 /* vi: set ts=4 sw=4 expandtab: */

+ 3 - 1
Engine/lib/sdl/src/events/SDL_dropevents_c.h

@@ -23,7 +23,9 @@
 #ifndef _SDL_dropevents_c_h
 #define _SDL_dropevents_c_h
 
-extern int SDL_SendDropFile(const char *file);
+extern int SDL_SendDropFile(SDL_Window *window, const char *file);
+extern int SDL_SendDropText(SDL_Window *window, const char *text);
+extern int SDL_SendDropComplete(SDL_Window *window);
 
 #endif /* _SDL_dropevents_c_h */
 

+ 59 - 58
Engine/lib/sdl/src/events/SDL_events.c

@@ -73,15 +73,15 @@ typedef struct _SDL_SysWMEntry
 static struct
 {
     SDL_mutex *lock;
-    volatile SDL_bool active;
-    volatile int count;
-    volatile int max_events_seen;
+    SDL_atomic_t active;
+    SDL_atomic_t count;
+    int max_events_seen;
     SDL_EventEntry *head;
     SDL_EventEntry *tail;
     SDL_EventEntry *free;
     SDL_SysWMEntry *wmmsg_used;
     SDL_SysWMEntry *wmmsg_free;
-} SDL_EventQ = { NULL, SDL_TRUE, 0, 0, NULL, NULL, NULL, NULL, NULL };
+} SDL_EventQ = { NULL, { 1 }, { 0 }, 0, NULL, NULL, NULL, NULL, NULL };
 
 
 /* Public functions */
@@ -98,7 +98,7 @@ SDL_StopEventLoop(void)
         SDL_LockMutex(SDL_EventQ.lock);
     }
 
-    SDL_EventQ.active = SDL_FALSE;
+    SDL_AtomicSet(&SDL_EventQ.active, 0);
 
     if (report && SDL_atoi(report)) {
         SDL_Log("SDL EVENT QUEUE: Maximum events in-flight: %d\n",
@@ -127,7 +127,7 @@ SDL_StopEventLoop(void)
         wmmsg = next;
     }
 
-    SDL_EventQ.count = 0;
+    SDL_AtomicSet(&SDL_EventQ.count, 0);
     SDL_EventQ.max_events_seen = 0;
     SDL_EventQ.head = NULL;
     SDL_EventQ.tail = NULL;
@@ -171,7 +171,7 @@ SDL_StartEventLoop(void)
         SDL_EventQ.lock = SDL_CreateMutex();
     }
     if (SDL_EventQ.lock == NULL) {
-        return (-1);
+        return -1;
     }
 #endif /* !SDL_THREADS_DISABLED */
 
@@ -180,9 +180,9 @@ SDL_StartEventLoop(void)
     SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
 
-    SDL_EventQ.active = SDL_TRUE;
+    SDL_AtomicSet(&SDL_EventQ.active, 1);
 
-    return (0);
+    return 0;
 }
 
 
@@ -191,9 +191,11 @@ static int
 SDL_AddEvent(SDL_Event * event)
 {
     SDL_EventEntry *entry;
+    const int initial_count = SDL_AtomicGet(&SDL_EventQ.count);
+    int final_count;
 
-    if (SDL_EventQ.count >= SDL_MAX_QUEUED_EVENTS) {
-        SDL_SetError("Event queue is full (%d events)", SDL_EventQ.count);
+    if (initial_count >= SDL_MAX_QUEUED_EVENTS) {
+        SDL_SetError("Event queue is full (%d events)", initial_count);
         return 0;
     }
 
@@ -225,10 +227,10 @@ SDL_AddEvent(SDL_Event * event)
         entry->prev = NULL;
         entry->next = NULL;
     }
-    ++SDL_EventQ.count;
 
-    if (SDL_EventQ.count > SDL_EventQ.max_events_seen) {
-        SDL_EventQ.max_events_seen = SDL_EventQ.count;
+    final_count = SDL_AtomicAdd(&SDL_EventQ.count, 1) + 1;
+    if (final_count > SDL_EventQ.max_events_seen) {
+        SDL_EventQ.max_events_seen = final_count;
     }
 
     return 1;
@@ -256,8 +258,8 @@ SDL_CutEvent(SDL_EventEntry *entry)
 
     entry->next = SDL_EventQ.free;
     SDL_EventQ.free = entry;
-    SDL_assert(SDL_EventQ.count > 0);
-    --SDL_EventQ.count;
+    SDL_assert(SDL_AtomicGet(&SDL_EventQ.count) > 0);
+    SDL_AtomicAdd(&SDL_EventQ.count, -1);
 }
 
 /* Lock the event queue, take a peep at it, and unlock it */
@@ -268,7 +270,7 @@ SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
     int i, used;
 
     /* Don't look after we've quit */
-    if (!SDL_EventQ.active) {
+    if (!SDL_AtomicGet(&SDL_EventQ.active)) {
         /* We get a few spurious events at shutdown, so don't warn then */
         if (action != SDL_ADDEVENT) {
             SDL_SetError("The event system has been shut down");
@@ -285,56 +287,54 @@ SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
         } else {
             SDL_EventEntry *entry, *next;
             SDL_SysWMEntry *wmmsg, *wmmsg_next;
-            SDL_Event tmpevent;
             Uint32 type;
 
-            /* If 'events' is NULL, just see if they exist */
-            if (events == NULL) {
-                action = SDL_PEEKEVENT;
-                numevents = 1;
-                events = &tmpevent;
-            }
-
-            /* Clean out any used wmmsg data
-               FIXME: Do we want to retain the data for some period of time?
-             */
-            for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; wmmsg = wmmsg_next) {
-                wmmsg_next = wmmsg->next;
-                wmmsg->next = SDL_EventQ.wmmsg_free;
-                SDL_EventQ.wmmsg_free = wmmsg;
+            if (action == SDL_GETEVENT) {
+                /* Clean out any used wmmsg data
+                   FIXME: Do we want to retain the data for some period of time?
+                 */
+                for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; wmmsg = wmmsg_next) {
+                    wmmsg_next = wmmsg->next;
+                    wmmsg->next = SDL_EventQ.wmmsg_free;
+                    SDL_EventQ.wmmsg_free = wmmsg;
+                }
+                SDL_EventQ.wmmsg_used = NULL;
             }
-            SDL_EventQ.wmmsg_used = NULL;
 
-            for (entry = SDL_EventQ.head; entry && used < numevents; entry = next) {
+            for (entry = SDL_EventQ.head; entry && (!events || used < numevents); entry = next) {
                 next = entry->next;
                 type = entry->event.type;
                 if (minType <= type && type <= maxType) {
-                    events[used] = entry->event;
-                    if (entry->event.type == SDL_SYSWMEVENT) {
-                        /* We need to copy the wmmsg somewhere safe.
-                           For now we'll guarantee it's valid at least until
-                           the next call to SDL_PeepEvents()
-                         */
-                        if (SDL_EventQ.wmmsg_free) {
-                            wmmsg = SDL_EventQ.wmmsg_free;
-                            SDL_EventQ.wmmsg_free = wmmsg->next;
-                        } else {
-                            wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg));
+                    if (events) {
+                        events[used] = entry->event;
+                        if (entry->event.type == SDL_SYSWMEVENT) {
+                            /* We need to copy the wmmsg somewhere safe.
+                               For now we'll guarantee it's valid at least until
+                               the next call to SDL_PeepEvents()
+                             */
+                            if (SDL_EventQ.wmmsg_free) {
+                                wmmsg = SDL_EventQ.wmmsg_free;
+                                SDL_EventQ.wmmsg_free = wmmsg->next;
+                            } else {
+                                wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg));
+                            }
+                            wmmsg->msg = *entry->event.syswm.msg;
+                            wmmsg->next = SDL_EventQ.wmmsg_used;
+                            SDL_EventQ.wmmsg_used = wmmsg;
+                            events[used].syswm.msg = &wmmsg->msg;
                         }
-                        wmmsg->msg = *entry->event.syswm.msg;
-                        wmmsg->next = SDL_EventQ.wmmsg_used;
-                        SDL_EventQ.wmmsg_used = wmmsg;
-                        events[used].syswm.msg = &wmmsg->msg;
-                    }
-                    ++used;
 
-                    if (action == SDL_GETEVENT) {
-                        SDL_CutEvent(entry);
+                        if (action == SDL_GETEVENT) {
+                            SDL_CutEvent(entry);
+                        }
                     }
+                    ++used;
                 }
             }
         }
-        SDL_UnlockMutex(SDL_EventQ.lock);
+        if (SDL_EventQ.lock) {
+            SDL_UnlockMutex(SDL_EventQ.lock);
+        }
     } else {
         return SDL_SetError("Couldn't lock event queue");
     }
@@ -363,7 +363,7 @@ void
 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
 {
     /* Don't look after we've quit */
-    if (!SDL_EventQ.active) {
+    if (!SDL_AtomicGet(&SDL_EventQ.active)) {
         return;
     }
 
@@ -376,7 +376,7 @@ SDL_FlushEvents(Uint32 minType, Uint32 maxType)
 #endif
 
     /* Lock the event queue */
-    if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
+    if (SDL_EventQ.lock && SDL_LockMutex(SDL_EventQ.lock) == 0) {
         SDL_EventEntry *entry, *next;
         Uint32 type;
         for (entry = SDL_EventQ.head; entry; entry = next) {
@@ -437,8 +437,6 @@ SDL_WaitEventTimeout(SDL_Event * event, int timeout)
         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
         case -1:
             return 0;
-        case 1:
-            return 1;
         case 0:
             if (timeout == 0) {
                 /* Polling and no events, just return */
@@ -450,6 +448,9 @@ SDL_WaitEventTimeout(SDL_Event * event, int timeout)
             }
             SDL_Delay(10);
             break;
+        default:
+            /* Has events */
+            return 1;
         }
     }
 }

+ 1 - 1
Engine/lib/sdl/src/events/SDL_gesture.c

@@ -21,7 +21,7 @@
 
 #include "../SDL_internal.h"
 
-/* General mouse handling code for SDL */
+/* General gesture handling code for SDL */
 
 #include "SDL_events.h"
 #include "SDL_endian.h"

+ 37 - 33
Engine/lib/sdl/src/events/SDL_mouse.c

@@ -322,15 +322,13 @@ static SDL_MouseClickState *GetMouseClickState(SDL_Mouse *mouse, Uint8 button)
     return &mouse->clickstate[button];
 }
 
-int
-SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
+static int
+SDL_PrivateSendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
 {
     SDL_Mouse *mouse = SDL_GetMouse();
     int posted;
     Uint32 type;
     Uint32 buttonstate = mouse->buttonstate;
-    SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button);
-    Uint8 click_count;
 
     /* Figure out which event to perform */
     switch (state) {
@@ -358,25 +356,28 @@ SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8
     }
     mouse->buttonstate = buttonstate;
 
-    if (clickstate) {
-        if (state == SDL_PRESSED) {
-            Uint32 now = SDL_GetTicks();
+    if (clicks < 0) {
+        SDL_MouseClickState *clickstate = GetMouseClickState(mouse, button);
+        if (clickstate) {
+            if (state == SDL_PRESSED) {
+                Uint32 now = SDL_GetTicks();
 
-            if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + SDL_double_click_time) ||
-                SDL_abs(mouse->x - clickstate->last_x) > SDL_double_click_radius ||
-                SDL_abs(mouse->y - clickstate->last_y) > SDL_double_click_radius) {
-                clickstate->click_count = 0;
-            }
-            clickstate->last_timestamp = now;
-            clickstate->last_x = mouse->x;
-            clickstate->last_y = mouse->y;
-            if (clickstate->click_count < 255) {
-                ++clickstate->click_count;
+                if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + SDL_double_click_time) ||
+                    SDL_abs(mouse->x - clickstate->last_x) > SDL_double_click_radius ||
+                    SDL_abs(mouse->y - clickstate->last_y) > SDL_double_click_radius) {
+                    clickstate->click_count = 0;
+                }
+                clickstate->last_timestamp = now;
+                clickstate->last_x = mouse->x;
+                clickstate->last_y = mouse->y;
+                if (clickstate->click_count < 255) {
+                    ++clickstate->click_count;
+                }
             }
+            clicks = clickstate->click_count;
+        } else {
+            clicks = 1;
         }
-        click_count = clickstate->click_count;
-    } else {
-        click_count = 1;
     }
 
     /* Post the event, if desired */
@@ -388,7 +389,7 @@ SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8
         event.button.which = mouseID;
         event.button.state = state;
         event.button.button = button;
-        event.button.clicks = click_count;
+        event.button.clicks = (Uint8) SDL_min(clicks, 255);
         event.button.x = mouse->x;
         event.button.y = mouse->y;
         posted = (SDL_PushEvent(&event) > 0);
@@ -398,10 +399,23 @@ SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8
     if (window && state == SDL_RELEASED) {
         SDL_UpdateMouseFocus(window, mouse->x, mouse->y, buttonstate);
     }
-
+    
     return posted;
 }
 
+int
+SDL_SendMouseButtonClicks(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks)
+{
+    clicks = SDL_max(clicks, 0);
+    return SDL_PrivateSendMouseButton(window, mouseID, state, button, clicks);
+}
+
+int
+SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button)
+{
+    return SDL_PrivateSendMouseButton(window, mouseID, state, button, -1);
+}
+
 int
 SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction)
 {
@@ -550,21 +564,11 @@ SDL_WarpMouseGlobal(int x, int y)
 static SDL_bool
 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
 {
-    const char *hint;
-
     if (!mouse->SetRelativeMouseMode) {
         return SDL_TRUE;
     }
 
-    hint = SDL_GetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP);
-    if (hint) {
-        if (*hint == '0') {
-            return SDL_FALSE;
-        } else {
-            return SDL_TRUE;
-        }
-    }
-    return SDL_FALSE;
+    return SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, SDL_FALSE);
 }
 
 int

+ 3 - 0
Engine/lib/sdl/src/events/SDL_mouse_c.h

@@ -119,6 +119,9 @@ extern int SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int rel
 /* Send a mouse button event */
 extern int SDL_SendMouseButton(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button);
 
+/* Send a mouse button event with a click count */
+extern int SDL_SendMouseButtonClicks(SDL_Window * window, SDL_MouseID mouseID, Uint8 state, Uint8 button, int clicks);
+
 /* Send a mouse wheel event */
 extern int SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, int x, int y, SDL_MouseWheelDirection direction);
 

+ 1 - 3
Engine/lib/sdl/src/events/SDL_quit.c

@@ -91,9 +91,7 @@ SDL_QuitInit_Internal(void)
 int
 SDL_QuitInit(void)
 {
-    const char *hint = SDL_GetHint(SDL_HINT_NO_SIGNAL_HANDLERS);
-    disable_signals = hint && (SDL_atoi(hint) == 1);
-    if (!disable_signals) {
+    if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
         return SDL_QuitInit_Internal();
     }
     return 0;

+ 17 - 1
Engine/lib/sdl/src/events/SDL_windowevents.c

@@ -70,6 +70,20 @@ RemovePendingMoveEvents(void * userdata, SDL_Event *event)
     return 1;
 }
 
+static int
+RemovePendingExposedEvents(void * userdata, SDL_Event *event)
+{
+    SDL_Event *new_event = (SDL_Event *)userdata;
+
+    if (event->type == SDL_WINDOWEVENT &&
+        event->window.event == SDL_WINDOWEVENT_EXPOSED &&
+        event->window.windowID == new_event->window.windowID) {
+        /* We're about to post a new exposed event, drop the old one */
+        return 0;
+    }
+    return 1;
+}
+
 int
 SDL_SendWindowEvent(SDL_Window * window, Uint8 windowevent, int data1,
                     int data2)
@@ -195,7 +209,9 @@ SDL_SendWindowEvent(SDL_Window * window, Uint8 windowevent, int data1,
         if (windowevent == SDL_WINDOWEVENT_MOVED) {
             SDL_FilterEvents(RemovePendingMoveEvents, &event);
         }
-
+        if (windowevent == SDL_WINDOWEVENT_EXPOSED) {
+            SDL_FilterEvents(RemovePendingExposedEvents, &event);
+        }
         posted = (SDL_PushEvent(&event) > 0);
     }
 

+ 7 - 7
Engine/lib/sdl/src/events/scancodes_linux.h

@@ -111,7 +111,7 @@ static SDL_Scancode const linux_scancode_table[] = {
     /*  82 */    SDL_SCANCODE_KP_0,
     /*  83 */    SDL_SCANCODE_KP_PERIOD,
     0,
-    /*  85 */    SDL_SCANCODE_UNKNOWN, /* KEY_ZENKAKUHANKAKU */
+    /*  85 */    SDL_SCANCODE_LANG5, /* KEY_ZENKAKUHANKAKU */
     /*  86 */    SDL_SCANCODE_NONUSBACKSLASH, /* KEY_102ND */
     /*  87 */    SDL_SCANCODE_F11,
     /*  88 */    SDL_SCANCODE_F12,
@@ -153,7 +153,7 @@ static SDL_Scancode const linux_scancode_table[] = {
     /*  124 */    SDL_SCANCODE_INTERNATIONAL3, /* KEY_YEN */
     /*  125 */    SDL_SCANCODE_LGUI,
     /*  126 */    SDL_SCANCODE_RGUI,
-    /*  127 */    SDL_SCANCODE_UNKNOWN, /* KEY_COMPOSE */
+    /*  127 */    SDL_SCANCODE_APPLICATION, /* KEY_COMPOSE */
     /*  128 */    SDL_SCANCODE_STOP,
     /*  129 */    SDL_SCANCODE_AGAIN,
     /*  130 */    SDL_SCANCODE_UNKNOWN, /* KEY_PROPS */
@@ -174,9 +174,9 @@ static SDL_Scancode const linux_scancode_table[] = {
     /*  145 */    SDL_SCANCODE_UNKNOWN, /* KEY_SENDFILE */
     /*  146 */    SDL_SCANCODE_UNKNOWN, /* KEY_DELETEFILE */
     /*  147 */    SDL_SCANCODE_UNKNOWN, /* KEY_XFER */
-    /*  148 */    SDL_SCANCODE_UNKNOWN, /* KEY_PROG1 */
-    /*  149 */    SDL_SCANCODE_UNKNOWN, /* KEY_PROG2 */
-    /*  150 */    SDL_SCANCODE_UNKNOWN, /* KEY_WWW */
+    /*  148 */    SDL_SCANCODE_APP1, /* KEY_PROG1 */
+    /*  149 */    SDL_SCANCODE_APP2, /* KEY_PROG2 */
+    /*  150 */    SDL_SCANCODE_WWW, /* KEY_WWW */
     /*  151 */    SDL_SCANCODE_UNKNOWN, /* KEY_MSDOS */
     /*  152 */    SDL_SCANCODE_UNKNOWN, /* KEY_COFFEE */
     /*  153 */    SDL_SCANCODE_UNKNOWN, /* KEY_DIRECTION */
@@ -192,7 +192,7 @@ static SDL_Scancode const linux_scancode_table[] = {
     /*  163 */    SDL_SCANCODE_AUDIONEXT, /* KEY_NEXTSONG */
     /*  164 */    SDL_SCANCODE_AUDIOPLAY, /* KEY_PLAYPAUSE */
     /*  165 */    SDL_SCANCODE_AUDIOPREV, /* KEY_PREVIOUSSONG */
-    /*  166 */    SDL_SCANCODE_UNKNOWN, /* KEY_STOPCD */
+    /*  166 */    SDL_SCANCODE_AUDIOSTOP, /* KEY_STOPCD */
     /*  167 */    SDL_SCANCODE_UNKNOWN, /* KEY_RECORD */
     /*  168 */    SDL_SCANCODE_UNKNOWN, /* KEY_REWIND */
     /*  169 */    SDL_SCANCODE_UNKNOWN, /* KEY_PHONE */
@@ -221,7 +221,7 @@ static SDL_Scancode const linux_scancode_table[] = {
     /*  192 */    SDL_SCANCODE_F22,
     /*  193 */    SDL_SCANCODE_F23,
     /*  194 */    SDL_SCANCODE_F24,
-    0, 0, 0, 0,
+    0, 0, 0, 0, 0,
     /*  200 */    SDL_SCANCODE_UNKNOWN, /* KEY_PLAYCD */
     /*  201 */    SDL_SCANCODE_UNKNOWN, /* KEY_PAUSECD */
     /*  202 */    SDL_SCANCODE_UNKNOWN, /* KEY_PROG3 */

+ 85 - 0
Engine/lib/sdl/src/events/scancodes_xfree86.h

@@ -418,4 +418,89 @@ static const SDL_Scancode xfree86_scancode_table2[] = {
     /* 238 */   SDL_SCANCODE_UNKNOWN,   /* XF86WLAN */
 };
 
+/* Xvnc / Xtightvnc scancodes from xmodmap -pk */
+static const SDL_Scancode xvnc_scancode_table[] = {
+    /*  0 */    SDL_SCANCODE_LCTRL,
+    /*  1 */    SDL_SCANCODE_RCTRL,
+    /*  2 */    SDL_SCANCODE_LSHIFT,
+    /*  3 */    SDL_SCANCODE_RSHIFT,
+    /*  4 */    SDL_SCANCODE_UNKNOWN, /* Meta_L */
+    /*  5 */    SDL_SCANCODE_UNKNOWN, /* Meta_R */
+    /*  6 */    SDL_SCANCODE_LALT,
+    /*  7 */    SDL_SCANCODE_RALT,
+    /*  8 */    SDL_SCANCODE_SPACE,
+    /*  9 */    SDL_SCANCODE_0,
+    /*  10 */   SDL_SCANCODE_1,
+    /*  11 */   SDL_SCANCODE_2,
+    /*  12 */   SDL_SCANCODE_3,
+    /*  13 */   SDL_SCANCODE_4,
+    /*  14 */   SDL_SCANCODE_5,
+    /*  15 */   SDL_SCANCODE_6,
+    /*  16 */   SDL_SCANCODE_7,
+    /*  17 */   SDL_SCANCODE_8,
+    /*  18 */   SDL_SCANCODE_9,
+    /*  19 */   SDL_SCANCODE_MINUS,
+    /*  20 */   SDL_SCANCODE_EQUALS,
+    /*  21 */   SDL_SCANCODE_LEFTBRACKET,
+    /*  22 */   SDL_SCANCODE_RIGHTBRACKET,
+    /*  23 */   SDL_SCANCODE_SEMICOLON,
+    /*  24 */   SDL_SCANCODE_APOSTROPHE,
+    /*  25 */   SDL_SCANCODE_GRAVE,
+    /*  26 */   SDL_SCANCODE_COMMA,
+    /*  27 */   SDL_SCANCODE_PERIOD,
+    /*  28 */   SDL_SCANCODE_SLASH,
+    /*  29 */   SDL_SCANCODE_BACKSLASH,
+    /*  30 */   SDL_SCANCODE_A,
+    /*  31 */   SDL_SCANCODE_B,
+    /*  32 */   SDL_SCANCODE_C,
+    /*  33 */   SDL_SCANCODE_D,
+    /*  34 */   SDL_SCANCODE_E,
+    /*  35 */   SDL_SCANCODE_F,
+    /*  36 */   SDL_SCANCODE_G,
+    /*  37 */   SDL_SCANCODE_H,
+    /*  38 */   SDL_SCANCODE_I,
+    /*  39 */   SDL_SCANCODE_J,
+    /*  40 */   SDL_SCANCODE_K,
+    /*  41 */   SDL_SCANCODE_L,
+    /*  42 */   SDL_SCANCODE_M,
+    /*  43 */   SDL_SCANCODE_N,
+    /*  44 */   SDL_SCANCODE_O,
+    /*  45 */   SDL_SCANCODE_P,
+    /*  46 */   SDL_SCANCODE_Q,
+    /*  47 */   SDL_SCANCODE_R,
+    /*  48 */   SDL_SCANCODE_S,
+    /*  49 */   SDL_SCANCODE_T,
+    /*  50 */   SDL_SCANCODE_U,
+    /*  51 */   SDL_SCANCODE_V,
+    /*  52 */   SDL_SCANCODE_W,
+    /*  53 */   SDL_SCANCODE_X,
+    /*  54 */   SDL_SCANCODE_Y,
+    /*  55 */   SDL_SCANCODE_Z,
+    /*  56 */   SDL_SCANCODE_BACKSPACE,
+    /*  57 */   SDL_SCANCODE_RETURN,
+    /*  58 */   SDL_SCANCODE_TAB,
+    /*  59 */   SDL_SCANCODE_ESCAPE,
+    /*  60 */   SDL_SCANCODE_DELETE,
+    /*  61 */   SDL_SCANCODE_HOME,
+    /*  62 */   SDL_SCANCODE_END,
+    /*  63 */   SDL_SCANCODE_PAGEUP,
+    /*  64 */   SDL_SCANCODE_PAGEDOWN,
+    /*  65 */   SDL_SCANCODE_UP,
+    /*  66 */   SDL_SCANCODE_DOWN,
+    /*  67 */   SDL_SCANCODE_LEFT,
+    /*  68 */   SDL_SCANCODE_RIGHT,
+    /*  69 */   SDL_SCANCODE_F1,
+    /*  70 */   SDL_SCANCODE_F2,
+    /*  71 */   SDL_SCANCODE_F3,
+    /*  72 */   SDL_SCANCODE_F4,
+    /*  73 */   SDL_SCANCODE_F5,
+    /*  74 */   SDL_SCANCODE_F6,
+    /*  75 */   SDL_SCANCODE_F7,
+    /*  76 */   SDL_SCANCODE_F8,
+    /*  77 */   SDL_SCANCODE_F9,
+    /*  78 */   SDL_SCANCODE_F10,
+    /*  79 */   SDL_SCANCODE_F11,
+    /*  80 */   SDL_SCANCODE_F12,
+};
+
 /* *INDENT-ON* */

+ 2 - 2
Engine/lib/sdl/src/filesystem/dummy/SDL_sysfilesystem.c

@@ -20,7 +20,7 @@
 */
 #include "../../SDL_internal.h"
 
-#ifdef SDL_FILESYSTEM_DUMMY
+#if defined(SDL_FILESYSTEM_DUMMY) || defined(SDL_FILESYSTEM_DISABLED)
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 /* System dependent filesystem routines                                */
@@ -42,6 +42,6 @@ SDL_GetPrefPath(const char *org, const char *app)
     return NULL;
 }
 
-#endif /* SDL_FILESYSTEM_DUMMY */
+#endif /* SDL_FILESYSTEM_DUMMY || SDL_FILESYSTEM_DISABLED */
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 21 - 2
Engine/lib/sdl/src/filesystem/unix/SDL_sysfilesystem.c

@@ -33,7 +33,7 @@
 #include <sys/types.h>
 #include <limits.h>
 
-#ifdef __FREEBSD__
+#if defined(__FREEBSD__) || defined(__OPENBSD__)
 #include <sys/sysctl.h>
 #endif
 
@@ -90,7 +90,26 @@ SDL_GetBasePath(void)
             return NULL;
         }
     }
-#elif defined(__SOLARIS__)
+#endif
+#if defined(__OPENBSD__)
+    char **retvalargs;
+    size_t len;
+    const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
+    if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) {
+        retvalargs = SDL_malloc(len);
+        if (!retvalargs) {
+            SDL_OutOfMemory();
+            return NULL;
+        }
+        sysctl(mib, 4, retvalargs, &len, NULL, 0);
+        retval = SDL_malloc(PATH_MAX + 1);
+        if (retval)
+            realpath(retvalargs[0], retval);
+
+        SDL_free(retvalargs);
+    }
+#endif
+#if defined(__SOLARIS__)
     const char *path = getexecname();
     if ((path != NULL) && (path[0] == '/')) { /* must be absolute path... */
         retval = SDL_strdup(path);

+ 2 - 2
Engine/lib/sdl/src/haptic/linux/SDL_syshaptic.c

@@ -609,7 +609,7 @@ SDL_SYS_HapticQuit(void)
         /* Opened and not closed haptics are leaked, this is on purpose.
          * Close your haptic devices after usage. */
         SDL_free(item->fname);
-        item->fname = NULL;
+        SDL_free(item);
     }
 
 #if SDL_USE_LIBUDEV
@@ -690,7 +690,7 @@ SDL_SYS_ToDirection(Uint16 *dest, SDL_HapticDirection * src)
         else if (!src->dir[0])
             *dest = (src->dir[1] >= 0 ? 0x8000 : 0);
         else {
-            float f = atan2(src->dir[1], src->dir[0]);    /* Ideally we'd use fixed point math instead of floats... */
+            float f = SDL_atan2(src->dir[1], src->dir[0]);    /* Ideally we'd use fixed point math instead of floats... */
                     /*
                       atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
                        - Y-axis-value is the second coordinate (from center to SOUTH)

+ 10 - 9
Engine/lib/sdl/src/haptic/windows/SDL_dinputhaptic.c

@@ -325,20 +325,12 @@ SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device
 
         /* Set data format. */
         ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device,
-                                                &c_dfDIJoystick2);
+                                                &SDL_c_dfDIJoystick2);
         if (FAILED(ret)) {
             DI_SetError("Setting data format", ret);
             goto acquire_err;
         }
 
-        /* Get number of axes. */
-        ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
-                                              DI_DeviceObjectCallback,
-                                              haptic, DIDFT_AXIS);
-        if (FAILED(ret)) {
-            DI_SetError("Getting device axes", ret);
-            goto acquire_err;
-        }
 
         /* Acquire the device. */
         ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
@@ -348,6 +340,15 @@ SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device
         }
     }
 
+    /* Get number of axes. */
+    ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
+                                          DI_DeviceObjectCallback,
+                                          haptic, DIDFT_AXIS);
+    if (FAILED(ret)) {
+        DI_SetError("Getting device axes", ret);
+        goto acquire_err;
+    }
+
     /* Reset all actuators - just in case. */
     ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
                                                        DISFFC_RESET);

+ 1 - 1
Engine/lib/sdl/src/haptic/windows/SDL_windowshaptic.c

@@ -255,7 +255,7 @@ SDL_SYS_HapticQuit(void)
     for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) {
         if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) {
             /* we _have_ to stop the thread before we free the XInput DLL! */
-            hapticitem->hwdata->stopThread = 1;
+            SDL_AtomicSet(&hapticitem->hwdata->stopThread, 1);
             SDL_WaitThread(hapticitem->hwdata->thread, NULL);
             hapticitem->hwdata->thread = NULL;
         }

+ 2 - 2
Engine/lib/sdl/src/haptic/windows/SDL_windowshaptic_c.h

@@ -42,8 +42,8 @@ struct haptic_hwdata
     Uint8 userid; /* XInput userid index for this joystick */
     SDL_Thread *thread;
     SDL_mutex *mutex;
-    volatile Uint32 stopTicks;
-    volatile int stopThread;
+    Uint32 stopTicks;
+    SDL_atomic_t stopThread;
 };
 
 

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