diff --git a/libs/SDL2/Xcode-iOS/Demos/iOS Launch Screen.storyboard b/libs/SDL2/Xcode-iOS/Demos/iOS Launch Screen.storyboard
new file mode 100644
index 00000000..4d8722a6
--- /dev/null
+++ b/libs/SDL2/Xcode-iOS/Demos/iOS Launch Screen.storyboard
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/SDL2/src/audio/coreaudio/SDL_coreaudio.c b/libs/SDL2/src/audio/coreaudio/SDL_coreaudio.m
similarity index 52%
rename from libs/SDL2/src/audio/coreaudio/SDL_coreaudio.c
rename to libs/SDL2/src/audio/coreaudio/SDL_coreaudio.m
index 46b617dc..85129050 100644
--- a/libs/SDL2/src/audio/coreaudio/SDL_coreaudio.c
+++ b/libs/SDL2/src/audio/coreaudio/SDL_coreaudio.m
@@ -22,19 +22,19 @@
#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
-static void COREAUDIO_CloseDevice(_THIS);
-
#define CHECK_RESULT(msg) \
if (result != noErr) { \
- COREAUDIO_CloseDevice(this); \
SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
return 0; \
}
@@ -185,7 +185,7 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
#if DEBUG_COREAUDIO
printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
((iscapture) ? "capture" : "output"),
- (int) *devCount, ptr, (int) dev);
+ (int) i, ptr, (int) dev);
#endif
addfn(ptr, iscapture, dev, addfndata);
}
@@ -268,42 +268,147 @@ device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectP
}
#endif
-/* The CoreAudio callback */
-static OSStatus
-outputCallback(void *inRefCon,
- AudioUnitRenderActionFlags * ioActionFlags,
- const AudioTimeStamp * inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList * ioData)
-{
- SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
- AudioBuffer *abuf;
- UInt32 remaining, len;
- void *ptr;
- UInt32 i;
- /* Only do anything if audio is enabled and not paused */
- if (!this->enabled || this->paused) {
- for (i = 0; i < ioData->mNumberBuffers; i++) {
- abuf = &ioData->mBuffers[i];
- SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
+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 0;
}
- /* No SDL conversion should be needed here, ever, since we accept
- any input format in OpenAudio, and leave the conversion to CoreAudio.
- */
- /*
- SDL_assert(!this->convert.needed);
- SDL_assert(this->spec.channels == ioData->mNumberChannels);
- */
+ 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;
- for (i = 0; i < ioData->mNumberBuffers; i++) {
- abuf = &ioData->mBuffers[i];
- remaining = abuf->mDataByteSize;
- ptr = abuf->mData;
while (remaining > 0) {
+ UInt32 len;
if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
/* Generate the data */
SDL_LockMutex(this->mixer_lock);
@@ -314,29 +419,56 @@ outputCallback(void *inRefCon,
}
len = this->hidden->bufferSize - this->hidden->bufferOffset;
- if (len > remaining)
+ if (len > remaining) {
len = remaining;
+ }
SDL_memcpy(ptr, (char *)this->hidden->buffer +
this->hidden->bufferOffset, len);
- ptr = (char *)ptr + len;
+ ptr = ptr + len;
remaining -= len;
this->hidden->bufferOffset += len;
}
}
- return 0;
+ if (!SDL_AtomicGet(&this->hidden->shutdown)) {
+ AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+ }
+
+ inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
}
-static OSStatus
-inputCallback(void *inRefCon,
- AudioUnitRenderActionFlags * ioActionFlags,
- const AudioTimeStamp * inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList * ioData)
+static void
+inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
+ const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
+ const AudioStreamPacketDescription *inPacketDescs )
{
- /* err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer); */
- /* !!! FIXME: write me! */
- return noErr;
+ 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);
+ }
}
@@ -357,7 +489,7 @@ device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectProperty
UInt32 size = sizeof (isAlive);
OSStatus error;
- if (!this->enabled) {
+ if (!SDL_AtomicGet(&this->enabled)) {
return 0; /* already known to be dead. */
}
@@ -381,43 +513,46 @@ device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectProperty
static void
COREAUDIO_CloseDevice(_THIS)
{
- if (this->hidden != NULL) {
- if (this->hidden->audioUnitOpened) {
- #if MACOSX_COREAUDIO
- /* Unregister our disconnect callback. */
- AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
- #endif
+ const SDL_bool iscapture = this->iscapture;
+ int i;
- AURenderCallbackStruct callback;
- const AudioUnitElement output_bus = 0;
- const AudioUnitElement input_bus = 1;
- const int iscapture = this->iscapture;
- const AudioUnitElement bus =
- ((iscapture) ? input_bus : output_bus);
- const AudioUnitScope scope =
- ((iscapture) ? kAudioUnitScope_Output :
- kAudioUnitScope_Input);
+/* !!! 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
- /* stop processing the audio unit */
- AudioOutputUnitStop(this->hidden->audioUnit);
+#if !MACOSX_COREAUDIO
+ update_audio_session(this, SDL_FALSE);
+#endif
- /* Remove the input callback */
- SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
- AudioUnitSetProperty(this->hidden->audioUnit,
- kAudioUnitProperty_SetRenderCallback,
- scope, bus, &callback, sizeof(callback));
+ if (this->hidden->thread) {
+ SDL_AtomicSet(&this->hidden->shutdown, 1);
+ SDL_WaitThread(this->hidden->thread, NULL);
+ }
- #if MACOSX_COREAUDIO
- CloseComponent(this->hidden->audioUnit);
- #else
- AudioComponentInstanceDispose(this->hidden->audioUnit);
- #endif
-
- this->hidden->audioUnitOpened = 0;
+ 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]);
+ }
}
- SDL_free(this->hidden->buffer);
- SDL_free(this->hidden);
- this->hidden = NULL;
+ 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--;
}
}
@@ -477,116 +612,105 @@ prepare_device(_THIS, void *handle, int iscapture)
#endif
static int
-prepare_audiounit(_THIS, void *handle, int iscapture,
- const AudioStreamBasicDescription * strdesc)
+prepare_audioqueue(_THIS)
{
- OSStatus result = noErr;
- AURenderCallbackStruct callback;
-#if MACOSX_COREAUDIO
- ComponentDescription desc;
- Component comp = NULL;
-#else
- AudioComponentDescription desc;
- AudioComponent comp = NULL;
-#endif
- const AudioUnitElement output_bus = 0;
- const AudioUnitElement input_bus = 1;
- const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
- const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
- kAudioUnitScope_Input);
+ const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
+ const int iscapture = this->iscapture;
+ OSStatus result;
+ int i;
-#if MACOSX_COREAUDIO
- if (!prepare_device(this, handle, iscapture)) {
- return 0;
- }
-#endif
+ SDL_assert(CFRunLoopGetCurrent() != NULL);
- SDL_zero(desc);
- desc.componentType = kAudioUnitType_Output;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
-
-#if MACOSX_COREAUDIO
- desc.componentSubType = kAudioUnitSubType_DefaultOutput;
- comp = FindNextComponent(NULL, &desc);
-#else
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
- comp = AudioComponentFindNext(NULL, &desc);
-#endif
-
- if (comp == NULL) {
- SDL_SetError("Couldn't find requested CoreAudio component");
- return 0;
+ 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");
}
- /* Open & initialize the audio unit */
#if MACOSX_COREAUDIO
- result = OpenAComponent(comp, &this->hidden->audioUnit);
- CHECK_RESULT("OpenAComponent");
-#else
- /*
- AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6
- We can't use OpenAComponent on iPhone because it is not present
- */
- result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
- CHECK_RESULT("AudioComponentInstanceNew");
+{
+ 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
- this->hidden->audioUnitOpened = 1;
-
-#if MACOSX_COREAUDIO
- result = AudioUnitSetProperty(this->hidden->audioUnit,
- kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, 0,
- &this->hidden->deviceID,
- sizeof(AudioDeviceID));
- CHECK_RESULT
- ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
-#endif
-
- /* Set the data format of the audio unit. */
- result = AudioUnitSetProperty(this->hidden->audioUnit,
- kAudioUnitProperty_StreamFormat,
- scope, bus, strdesc, sizeof(*strdesc));
- CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
-
- /* Set the audio callback */
- SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
- callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
- callback.inputProcRefCon = this;
- result = AudioUnitSetProperty(this->hidden->audioUnit,
- kAudioUnitProperty_SetRenderCallback,
- scope, bus, &callback, sizeof(callback));
- CHECK_RESULT
- ("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)");
-
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&this->spec);
/* Allocate a sample buffer */
- this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
+ 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;
+ }
- result = AudioUnitInitialize(this->hidden->audioUnit);
- CHECK_RESULT("AudioUnitInitialize");
+ 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");
+ }
- /* Finally, start processing of the audio unit */
- result = AudioOutputUnitStart(this->hidden->audioUnit);
- CHECK_RESULT("AudioOutputUnitStart");
-
-#if MACOSX_COREAUDIO
- /* Fire a callback if the device stops being "alive" (disconnected, etc). */
- AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
-#endif
+ 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;
+ AudioStreamBasicDescription *strdesc;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
int valid_datatype = 0;
@@ -596,15 +720,29 @@ COREAUDIO_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);
+
+ 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_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
- strdesc.mFormatID = kAudioFormatLinearPCM;
- strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
- strdesc.mChannelsPerFrame = this->spec.channels;
- strdesc.mSampleRate = this->spec.freq;
- strdesc.mFramesPerPacket = 1;
+ 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;
@@ -621,34 +759,53 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
case AUDIO_F32LSB:
case AUDIO_F32MSB:
valid_datatype = 1;
- strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
+ strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
- strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+ strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
if (SDL_AUDIO_ISFLOAT(this->spec.format))
- strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
+ strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
else if (SDL_AUDIO_ISSIGNED(this->spec.format))
- strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+ strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
break;
}
}
if (!valid_datatype) { /* shouldn't happen, but just in case... */
- COREAUDIO_CloseDevice(this);
return SDL_SetError("Unsupported audio format");
}
- strdesc.mBytesPerFrame =
- strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
- strdesc.mBytesPerPacket =
- strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
+ strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
+ strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
- if (!prepare_audiounit(this, handle, iscapture, &strdesc)) {
- COREAUDIO_CloseDevice(this);
- return -1; /* prepare_audiounit() will call SDL_SetError()... */
+#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. */
}
- return 0; /* good to go. */
+ 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
@@ -674,17 +831,11 @@ COREAUDIO_Init(SDL_AudioDriverImpl * impl)
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
#else
impl->OnlyHasDefaultOutputDevice = 1;
-
- /* Set category to ambient sound 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.
- */
- AudioSessionInitialize(NULL, NULL, NULL, nil);
- UInt32 category = kAudioSessionCategory_AmbientSound;
- AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
+ impl->OnlyHasDefaultCaptureDevice = 1;
#endif
impl->ProvidesOwnCallbackThread = 1;
+ impl->HasCaptureSupport = 1;
return 1; /* this audio target is available. */
}
diff --git a/libs/SDL2/src/core/linux/SDL_fcitx.c b/libs/SDL2/src/core/linux/SDL_fcitx.c
new file mode 100644
index 00000000..83d19e69
--- /dev/null
+++ b/libs/SDL2/src/core/linux/SDL_fcitx.c
@@ -0,0 +1,553 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ 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
+#include
+
+#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: */
diff --git a/libs/SDL2/src/core/linux/SDL_fcitx.h b/libs/SDL2/src/core/linux/SDL_fcitx.h
new file mode 100644
index 00000000..64020475
--- /dev/null
+++ b/libs/SDL2/src/core/linux/SDL_fcitx.h
@@ -0,0 +1,40 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ 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: */
diff --git a/libs/SDL2/src/core/linux/SDL_ime.c b/libs/SDL2/src/core/linux/SDL_ime.c
new file mode 100644
index 00000000..049bd6e0
--- /dev/null
+++ b/libs/SDL2/src/core/linux/SDL_ime.c
@@ -0,0 +1,138 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ 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();
+}
diff --git a/libs/SDL2/src/core/linux/SDL_ime.h b/libs/SDL2/src/core/linux/SDL_ime.h
new file mode 100644
index 00000000..22b31de3
--- /dev/null
+++ b/libs/SDL2/src/core/linux/SDL_ime.h
@@ -0,0 +1,38 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ 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 */
diff --git a/libs/SDL2/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur b/libs/SDL2/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur
new file mode 100644
index 00000000..c6556b8a
Binary files /dev/null and b/libs/SDL2/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur differ
diff --git a/libs/SDL2/src/main/winrt/SDL2-WinRTResources.rc b/libs/SDL2/src/main/winrt/SDL2-WinRTResources.rc
new file mode 100644
index 00000000..ce8549f2
--- /dev/null
+++ b/libs/SDL2/src/main/winrt/SDL2-WinRTResources.rc
@@ -0,0 +1,3 @@
+#include "winres.h"
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+5000 CURSOR "SDL2-WinRTResource_BlankCursor.cur"
diff --git a/libs/SDL2/src/video/winrt/SDL_winrtgamebar.cpp b/libs/SDL2/src/video/winrt/SDL_winrtgamebar.cpp
new file mode 100644
index 00000000..215dfcc8
--- /dev/null
+++ b/libs/SDL2/src/video/winrt/SDL_winrtgamebar.cpp
@@ -0,0 +1,196 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_WINRT
+
+/* Windows includes */
+#include
+#include
+#include
+
+
+/* SDL includes */
+extern "C" {
+#include "SDL_mouse.h"
+#include "../SDL_sysvideo.h"
+}
+#include "SDL_winrtvideo_cpp.h"
+
+
+/* Game Bar events can come in off the main thread. Use the following
+ WinRT CoreDispatcher to deal with them on SDL's thread.
+*/
+static Platform::WeakReference WINRT_MainThreadDispatcher;
+
+
+/* Win10's initial SDK (the 10.0.10240.0 release) does not include references
+ to Game Bar APIs, as the Game Bar was released via Win10 10.0.10586.0.
+
+ Declare its WinRT/COM interface here, to allow compilation with earlier
+ Windows SDKs.
+*/
+MIDL_INTERFACE("1DB9A292-CC78-4173-BE45-B61E67283EA7")
+IGameBarStatics_ : public IInspectable
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE add_VisibilityChanged(
+ __FIEventHandler_1_IInspectable *handler,
+ Windows::Foundation::EventRegistrationToken *token) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE remove_VisibilityChanged(
+ Windows::Foundation::EventRegistrationToken token) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE add_IsInputRedirectedChanged(
+ __FIEventHandler_1_IInspectable *handler,
+ Windows::Foundation::EventRegistrationToken *token) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE remove_IsInputRedirectedChanged(
+ Windows::Foundation::EventRegistrationToken token) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_Visible(
+ boolean *value) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_IsInputRedirected(
+ boolean *value) = 0;
+};
+
+/* Declare the game bar's COM GUID */
+static GUID IID_IGameBarStatics_ = { MAKELONG(0xA292, 0x1DB9), 0xCC78, 0x4173, { 0xBE, 0x45, 0xB6, 0x1E, 0x67, 0x28, 0x3E, 0xA7 } };
+
+/* Retrieves a pointer to the game bar, or NULL if it is not available.
+ If a pointer is returned, it's ->Release() method must be called
+ after the caller has finished using it.
+*/
+static IGameBarStatics_ *
+WINRT_GetGameBar()
+{
+ wchar_t *wClassName = L"Windows.Gaming.UI.GameBar";
+ HSTRING hClassName;
+ IActivationFactory *pActivationFactory = NULL;
+ IGameBarStatics_ *pGameBar = NULL;
+ HRESULT hr;
+
+ hr = ::WindowsCreateString(wClassName, (UINT32)wcslen(wClassName), &hClassName);
+ if (FAILED(hr)) {
+ goto done;
+ }
+
+ hr = Windows::Foundation::GetActivationFactory(hClassName, &pActivationFactory);
+ if (FAILED(hr)) {
+ goto done;
+ }
+
+ pActivationFactory->QueryInterface(IID_IGameBarStatics_, (void **) &pGameBar);
+
+done:
+ if (pActivationFactory) {
+ pActivationFactory->Release();
+ }
+ if (hClassName) {
+ ::WindowsDeleteString(hClassName);
+ }
+ return pGameBar;
+}
+
+static void
+WINRT_HandleGameBarIsInputRedirected_MainThread()
+{
+ IGameBarStatics_ *gameBar;
+ boolean isInputRedirected = 0;
+ if (!WINRT_MainThreadDispatcher) {
+ /* The game bar event handler has been deregistered! */
+ return;
+ }
+ gameBar = WINRT_GetGameBar();
+ if (!gameBar) {
+ /* Shouldn't happen, but just in case... */
+ return;
+ }
+ if (SUCCEEDED(gameBar->get_IsInputRedirected(&isInputRedirected))) {
+ if ( ! isInputRedirected) {
+ /* Input-control is now back to the SDL app. Restore the cursor,
+ in case Windows does not (it does not in either Win10
+ 10.0.10240.0 or 10.0.10586.0, maybe later version(s) too.
+ */
+ SDL_Cursor *cursor = SDL_GetCursor();
+ SDL_SetCursor(cursor);
+ }
+ }
+ gameBar->Release();
+}
+
+static void
+WINRT_HandleGameBarIsInputRedirected_NonMainThread(Platform::Object ^ o1, Platform::Object ^o2)
+{
+ Windows::UI::Core::CoreDispatcher ^dispatcher = WINRT_MainThreadDispatcher.Resolve();
+ if (dispatcher) {
+ dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler(&WINRT_HandleGameBarIsInputRedirected_MainThread));
+ }
+}
+
+void
+WINRT_InitGameBar(_THIS)
+{
+ SDL_VideoData *driverdata = (SDL_VideoData *)_this->driverdata;
+ IGameBarStatics_ *gameBar = WINRT_GetGameBar();
+ if (gameBar) {
+ /* GameBar.IsInputRedirected events can come in via something other than
+ the main/SDL thread.
+
+ Get a WinRT 'CoreDispatcher' that can be used to call back into the
+ SDL thread.
+ */
+ WINRT_MainThreadDispatcher = Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher;
+ Windows::Foundation::EventHandler ^handler = \
+ ref new Windows::Foundation::EventHandler(&WINRT_HandleGameBarIsInputRedirected_NonMainThread);
+ __FIEventHandler_1_IInspectable * pHandler = reinterpret_cast<__FIEventHandler_1_IInspectable *>(handler);
+ gameBar->add_IsInputRedirectedChanged(pHandler, &driverdata->gameBarIsInputRedirectedToken);
+ gameBar->Release();
+ }
+}
+
+void
+WINRT_QuitGameBar(_THIS)
+{
+ SDL_VideoData *driverdata;
+ IGameBarStatics_ *gameBar;
+ if (!_this || !_this->driverdata) {
+ return;
+ }
+ gameBar = WINRT_GetGameBar();
+ if (!gameBar) {
+ return;
+ }
+ driverdata = (SDL_VideoData *)_this->driverdata;
+ if (driverdata->gameBarIsInputRedirectedToken.Value) {
+ gameBar->remove_IsInputRedirectedChanged(driverdata->gameBarIsInputRedirectedToken);
+ driverdata->gameBarIsInputRedirectedToken.Value = 0;
+ }
+ WINRT_MainThreadDispatcher = nullptr;
+ gameBar->Release();
+}
+
+#endif /* SDL_VIDEO_DRIVER_WINRT */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/libs/SDL2/src/audio/SDL_audiomem.h b/libs/SDL2/src/video/winrt/SDL_winrtgamebar_cpp.h
similarity index 74%
rename from libs/SDL2/src/audio/SDL_audiomem.h
rename to libs/SDL2/src/video/winrt/SDL_winrtgamebar_cpp.h
index 091d15c2..afcef37b 100644
--- a/libs/SDL2/src/audio/SDL_audiomem.h
+++ b/libs/SDL2/src/video/winrt/SDL_winrtgamebar_cpp.h
@@ -18,8 +18,18 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
-#include "../SDL_internal.h"
+#include "SDL_config.h"
+
+#ifndef _SDL_winrtgamebar_h
+#define _SDL_winrtgamebar_h
+
+#ifdef __cplusplus
+/* These are exported as C++ functions, rather than C, to fix a compilation
+ bug with MSVC 2013, for Windows 8.x builds. */
+extern void WINRT_InitGameBar(_THIS);
+extern void WINRT_QuitGameBar(_THIS);
+#endif
+
+#endif /* _SDL_winrtmouse_h */
-#define SDL_AllocAudioMem SDL_malloc
-#define SDL_FreeAudioMem SDL_free
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/libs/SDL2/test/testaudiocapture.c b/libs/SDL2/test/testaudiocapture.c
new file mode 100644
index 00000000..26321a71
--- /dev/null
+++ b/libs/SDL2/test/testaudiocapture.c
@@ -0,0 +1,165 @@
+/*
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ 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.
+*/
+#include "SDL.h"
+
+#include
+
+#ifdef __EMSCRIPTEN__
+#include
+#endif
+
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+static SDL_AudioSpec spec;
+static SDL_AudioDeviceID devid_in = 0;
+static SDL_AudioDeviceID devid_out = 0;
+
+static void
+loop()
+{
+ SDL_bool please_quit = SDL_FALSE;
+ SDL_Event e;
+
+ while (SDL_PollEvent(&e)) {
+ if (e.type == SDL_QUIT) {
+ please_quit = SDL_TRUE;
+ } else if (e.type == SDL_KEYDOWN) {
+ if (e.key.keysym.sym == SDLK_ESCAPE) {
+ please_quit = SDL_TRUE;
+ }
+ } else if (e.type == SDL_MOUSEBUTTONDOWN) {
+ if (e.button.button == 1) {
+ SDL_PauseAudioDevice(devid_out, SDL_TRUE);
+ SDL_PauseAudioDevice(devid_in, SDL_FALSE);
+ }
+ } else if (e.type == SDL_MOUSEBUTTONUP) {
+ if (e.button.button == 1) {
+ SDL_PauseAudioDevice(devid_in, SDL_TRUE);
+ SDL_PauseAudioDevice(devid_out, SDL_FALSE);
+ }
+ }
+ }
+
+ if (SDL_GetAudioDeviceStatus(devid_in) == SDL_AUDIO_PLAYING) {
+ SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
+ } else {
+ SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+ }
+ SDL_RenderClear(renderer);
+ SDL_RenderPresent(renderer);
+
+ if (please_quit) {
+ /* stop playing back, quit. */
+ SDL_Log("Shutting down.\n");
+ SDL_PauseAudioDevice(devid_in, 1);
+ SDL_CloseAudioDevice(devid_in);
+ SDL_PauseAudioDevice(devid_out, 1);
+ SDL_CloseAudioDevice(devid_out);
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ #ifdef __EMSCRIPTEN__
+ emscripten_cancel_main_loop();
+ #endif
+ exit(0);
+ }
+
+ /* Note that it would be easier to just have a one-line function that
+ calls SDL_QueueAudio() as a capture device callback, but we're
+ trying to test the API, so we use SDL_DequeueAudio() here. */
+ while (SDL_TRUE) {
+ Uint8 buf[1024];
+ const Uint32 br = SDL_DequeueAudio(devid_in, buf, sizeof (buf));
+ SDL_QueueAudio(devid_out, buf, br);
+ if (br < sizeof (buf)) {
+ break;
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ /* (argv[1] == NULL means "open default device.") */
+ const char *devname = argv[1];
+ SDL_AudioSpec wanted;
+ int devcount;
+ int i;
+
+ /* Enable standard application logging */
+ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
+
+ /* Load the SDL library */
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
+ return (1);
+ }
+
+ window = SDL_CreateWindow("testaudiocapture", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 240, 0);
+ renderer = SDL_CreateRenderer(window, -1, 0);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+ SDL_RenderClear(renderer);
+ SDL_RenderPresent(renderer);
+
+ SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
+
+ devcount = SDL_GetNumAudioDevices(SDL_TRUE);
+ for (i = 0; i < devcount; i++) {
+ SDL_Log(" Capture device #%d: '%s'\n", i, SDL_GetAudioDeviceName(i, SDL_TRUE));
+ }
+
+ SDL_zero(wanted);
+ wanted.freq = 44100;
+ wanted.format = AUDIO_F32SYS;
+ wanted.channels = 1;
+ wanted.samples = 4096;
+ wanted.callback = NULL;
+
+ SDL_zero(spec);
+
+ /* DirectSound can fail in some instances if you open the same hardware
+ for both capture and output and didn't open the output end first,
+ according to the docs, so if you're doing something like this, always
+ open your capture devices second in case you land in those bizarre
+ circumstances. */
+
+ SDL_Log("Opening default playback device...\n");
+ devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
+ if (!devid_out) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for playback: %s!\n", SDL_GetError());
+ SDL_Quit();
+ exit(1);
+ }
+
+ SDL_Log("Opening capture device %s%s%s...\n",
+ devname ? "'" : "",
+ devname ? devname : "[[default]]",
+ devname ? "'" : "");
+
+ devid_in = SDL_OpenAudioDevice(argv[1], SDL_TRUE, &spec, &spec, 0);
+ if (!devid_in) {
+ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for capture: %s!\n", SDL_GetError());
+ SDL_Quit();
+ exit(1);
+ }
+
+ SDL_Log("Ready! Hold down mouse or finger to record!\n");
+
+#ifdef __EMSCRIPTEN__
+ emscripten_set_main_loop(loop, 0, 1);
+#else
+ while (1) { loop(); SDL_Delay(16); }
+#endif
+
+ return 0;
+}
+
diff --git a/libs/SDL2/test/testcustomcursor.c b/libs/SDL2/test/testcustomcursor.c
new file mode 100644
index 00000000..88b5c322
--- /dev/null
+++ b/libs/SDL2/test/testcustomcursor.c
@@ -0,0 +1,216 @@
+/*
+ Copyright (C) 1997-2016 Sam Lantinga
+
+ 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.
+*/
+
+#include
+#include
+
+#ifdef __EMSCRIPTEN__
+#include
+#endif
+
+#include "SDL_test_common.h"
+
+/* Stolen from the mailing list */
+/* Creates a new mouse cursor from an XPM */
+
+
+/* XPM */
+static const char *arrow[] = {
+ /* width height num_colors chars_per_pixel */
+ " 32 32 3 1",
+ /* colors */
+ "X c #000000",
+ ". c #ffffff",
+ " c None",
+ /* pixels */
+ "X ",
+ "XX ",
+ "X.X ",
+ "X..X ",
+ "X...X ",
+ "X....X ",
+ "X.....X ",
+ "X......X ",
+ "X.......X ",
+ "X........X ",
+ "X.....XXXXX ",
+ "X..X..X ",
+ "X.X X..X ",
+ "XX X..X ",
+ "X X..X ",
+ " X..X ",
+ " X..X ",
+ " X..X ",
+ " XX ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ "0,0"
+};
+
+static SDL_Cursor*
+init_color_cursor(const char *file)
+{
+ SDL_Cursor *cursor = NULL;
+ SDL_Surface *surface = SDL_LoadBMP(file);
+ if (surface) {
+ cursor = SDL_CreateColorCursor(surface, 0, 0);
+ SDL_FreeSurface(surface);
+ }
+ return cursor;
+}
+
+static SDL_Cursor*
+init_system_cursor(const char *image[])
+{
+ int i, row, col;
+ Uint8 data[4*32];
+ Uint8 mask[4*32];
+ int hot_x, hot_y;
+
+ i = -1;
+ for (row=0; row<32; ++row) {
+ for (col=0; col<32; ++col) {
+ if (col % 8) {
+ data[i] <<= 1;
+ mask[i] <<= 1;
+ } else {
+ ++i;
+ data[i] = mask[i] = 0;
+ }
+ switch (image[4+row][col]) {
+ case 'X':
+ data[i] |= 0x01;
+ mask[i] |= 0x01;
+ break;
+ case '.':
+ mask[i] |= 0x01;
+ break;
+ case ' ':
+ break;
+ }
+ }
+ }
+ sscanf(image[4+row], "%d,%d", &hot_x, &hot_y);
+ return SDL_CreateCursor(data, mask, 32, 32, hot_x, hot_y);
+}
+
+static SDLTest_CommonState *state;
+int done;
+SDL_Cursor *cursor = NULL;
+
+/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
+static void
+quit(int rc)
+{
+ SDLTest_CommonQuit(state);
+ exit(rc);
+}
+
+void
+loop()
+{
+ int i;
+ SDL_Event event;
+ /* Check for events */
+ while (SDL_PollEvent(&event)) {
+ SDLTest_CommonEvent(state, &event, &done);
+ }
+
+ for (i = 0; i < state->num_windows; ++i) {
+ SDL_Renderer *renderer = state->renderers[i];
+ SDL_RenderClear(renderer);
+ SDL_RenderPresent(renderer);
+ }
+#ifdef __EMSCRIPTEN__
+ if (done) {
+ emscripten_cancel_main_loop();
+ }
+#endif
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ const char *color_cursor = NULL;
+
+ /* Enable standard application logging */
+ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
+
+ /* Initialize test framework */
+ state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
+ if (!state) {
+ return 1;
+ }
+ for (i = 1; i < argc;) {
+ int consumed;
+
+ consumed = SDLTest_CommonArg(state, i);
+ if (consumed == 0) {
+ color_cursor = argv[i];
+ break;
+ }
+ if (consumed < 0) {
+ SDL_Log("Usage: %s %s\n", argv[0], SDLTest_CommonUsage(state));
+ quit(1);
+ }
+ i += consumed;
+ }
+
+ if (!SDLTest_CommonInit(state)) {
+ quit(2);
+ }
+
+ for (i = 0; i < state->num_windows; ++i) {
+ SDL_Renderer *renderer = state->renderers[i];
+ SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
+ SDL_RenderClear(renderer);
+ }
+
+ if (color_cursor) {
+ cursor = init_color_cursor(color_cursor);
+ } else {
+ cursor = init_system_cursor(arrow);
+ }
+ if (!cursor) {
+ SDL_Log("Error, couldn't create cursor\n");
+ quit(2);
+ }
+ SDL_SetCursor(cursor);
+
+ /* Main render loop */
+ done = 0;
+#ifdef __EMSCRIPTEN__
+ emscripten_set_main_loop(loop, 0, 1);
+#else
+ while (!done) {
+ loop();
+ }
+#endif
+
+ SDL_FreeCursor(cursor);
+ quit(0);
+
+ /* keep the compiler happy ... */
+ return(0);
+}