Browse Source

Update SDL2 to 2.0.5 hg.

Alex Szpakowski 9 years ago
parent
commit
25d889fa38

+ 40 - 0
libs/SDL2/Xcode-iOS/Demos/iOS Launch Screen.storyboard

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Default.png" translatesAutoresizingMaskIntoConstraints="NO" id="VeL-6u-rS3"/>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="VeL-6u-rS3" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="C5X-Vg-tvO"/>
+                            <constraint firstAttribute="trailing" secondItem="VeL-6u-rS3" secondAttribute="trailing" id="X4i-1U-3JE"/>
+                            <constraint firstItem="VeL-6u-rS3" firstAttribute="bottom" secondItem="xb3-aO-Qok" secondAttribute="top" id="dSu-2l-DcF"/>
+                            <constraint firstItem="VeL-6u-rS3" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="xKC-uj-bxE"/>
+                        </constraints>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="52" y="374.66266866566718"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="Default.png" width="320" height="480"/>
+    </resources>
+</document>

+ 348 - 197
libs/SDL2/src/audio/coreaudio/SDL_coreaudio.c → libs/SDL2/src/audio/coreaudio/SDL_coreaudio.m

@@ -22,19 +22,19 @@
 
 
 #if SDL_AUDIO_DRIVER_COREAUDIO
 #if SDL_AUDIO_DRIVER_COREAUDIO
 
 
+/* !!! FIXME: clean out some of the macro salsa in here. */
+
 #include "SDL_audio.h"
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_sysaudio.h"
 #include "../SDL_sysaudio.h"
 #include "SDL_coreaudio.h"
 #include "SDL_coreaudio.h"
 #include "SDL_assert.h"
 #include "SDL_assert.h"
+#include "../../thread/SDL_systhread.h"
 
 
 #define DEBUG_COREAUDIO 0
 #define DEBUG_COREAUDIO 0
 
 
-static void COREAUDIO_CloseDevice(_THIS);
-
 #define CHECK_RESULT(msg) \
 #define CHECK_RESULT(msg) \
     if (result != noErr) { \
     if (result != noErr) { \
-        COREAUDIO_CloseDevice(this); \
         SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
         SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
         return 0; \
         return 0; \
     }
     }
@@ -185,7 +185,7 @@ build_device_list(int iscapture, addDevFn addfn, void *addfndata)
 #if DEBUG_COREAUDIO
 #if DEBUG_COREAUDIO
             printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
             printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
                    ((iscapture) ? "capture" : "output"),
                    ((iscapture) ? "capture" : "output"),
-                   (int) *devCount, ptr, (int) dev);
+                   (int) i, ptr, (int) dev);
 #endif
 #endif
             addfn(ptr, iscapture, dev, addfndata);
             addfn(ptr, iscapture, dev, addfndata);
         }
         }
@@ -268,42 +268,147 @@ device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectP
 }
 }
 #endif
 #endif
 
 
-/* The CoreAudio callback */
-static OSStatus
-outputCallback(void *inRefCon,
-               AudioUnitRenderActionFlags * ioActionFlags,
-               const AudioTimeStamp * inTimeStamp,
-               UInt32 inBusNumber, UInt32 inNumberFrames,
-               AudioBufferList * ioData)
+
+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)
 {
 {
-    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);
+    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);
         }
         }
-        return 0;
     }
     }
+}
+
+- (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;
+        }
 
 
-    /* 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);
-     */
+        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;
 
 
-    for (i = 0; i < ioData->mNumberBuffers; i++) {
-        abuf = &ioData->mBuffers[i];
-        remaining = abuf->mDataByteSize;
-        ptr = abuf->mData;
         while (remaining > 0) {
         while (remaining > 0) {
+            UInt32 len;
             if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
             if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
                 /* Generate the data */
                 /* Generate the data */
                 SDL_LockMutex(this->mixer_lock);
                 SDL_LockMutex(this->mixer_lock);
@@ -314,29 +419,56 @@ outputCallback(void *inRefCon,
             }
             }
 
 
             len = this->hidden->bufferSize - this->hidden->bufferOffset;
             len = this->hidden->bufferSize - this->hidden->bufferOffset;
-            if (len > remaining)
+            if (len > remaining) {
                 len = remaining;
                 len = remaining;
+            }
             SDL_memcpy(ptr, (char *)this->hidden->buffer +
             SDL_memcpy(ptr, (char *)this->hidden->buffer +
                        this->hidden->bufferOffset, len);
                        this->hidden->bufferOffset, len);
-            ptr = (char *)ptr + len;
+            ptr = ptr + len;
             remaining -= len;
             remaining -= len;
             this->hidden->bufferOffset += 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);
     UInt32 size = sizeof (isAlive);
     OSStatus error;
     OSStatus error;
 
 
-    if (!this->enabled) {
+    if (!SDL_AtomicGet(&this->enabled)) {
         return 0;  /* already known to be dead. */
         return 0;  /* already known to be dead. */
     }
     }
 
 
@@ -381,43 +513,46 @@ device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectProperty
 static void
 static void
 COREAUDIO_CloseDevice(_THIS)
 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
-
-            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);
-
-            /* stop processing the audio unit */
-            AudioOutputUnitStop(this->hidden->audioUnit);
-
-            /* Remove the input callback */
-            SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
-            AudioUnitSetProperty(this->hidden->audioUnit,
-                                 kAudioUnitProperty_SetRenderCallback,
-                                 scope, bus, &callback, sizeof(callback));
-
-            #if MACOSX_COREAUDIO
-            CloseComponent(this->hidden->audioUnit);
-            #else
-            AudioComponentInstanceDispose(this->hidden->audioUnit);
-            #endif
-
-            this->hidden->audioUnitOpened = 0;
+    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]);
+            }
         }
         }
-        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
 #endif
 
 
 static int
 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);
-
-#if MACOSX_COREAUDIO
-    if (!prepare_device(this, handle, iscapture)) {
-        return 0;
-    }
-#endif
+    const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
+    const int iscapture = this->iscapture;
+    OSStatus result;
+    int i;
 
 
-    SDL_zero(desc);
-    desc.componentType = kAudioUnitType_Output;
-    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+    SDL_assert(CFRunLoopGetCurrent() != NULL);
 
 
-#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");
-#endif
-
-    this->hidden->audioUnitOpened = 1;
-
 #if MACOSX_COREAUDIO
 #if MACOSX_COREAUDIO
-    result = AudioUnitSetProperty(this->hidden->audioUnit,
-                                  kAudioOutputUnitProperty_CurrentDevice,
-                                  kAudioUnitScope_Global, 0,
-                                  &this->hidden->deviceID,
-                                  sizeof(AudioDeviceID));
-    CHECK_RESULT
-        ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
+{
+    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
 #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 */
     /* Calculate the final parameters for this audio specification */
     SDL_CalculateAudioSpec(&this->spec);
     SDL_CalculateAudioSpec(&this->spec);
 
 
     /* Allocate a sample buffer */
     /* Allocate a sample buffer */
-    this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
-    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
+    this->hidden->bufferSize = this->spec.size;
+    this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
 
 
-    result = AudioUnitInitialize(this->hidden->audioUnit);
-    CHECK_RESULT("AudioUnitInitialize");
+    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
+    if (this->hidden->buffer == NULL) {
+        SDL_OutOfMemory();
+        return 0;
+    }
 
 
-    /* Finally, start processing of the audio unit */
-    result = AudioOutputUnitStart(this->hidden->audioUnit);
-    CHECK_RESULT("AudioOutputUnitStart");
+    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");
+    }
 
 
-#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! */
     /* We're running! */
     return 1;
     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
 static int
 COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
 {
-    AudioStreamBasicDescription strdesc;
+    AudioStreamBasicDescription *strdesc;
     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
     int valid_datatype = 0;
     int valid_datatype = 0;
 
 
@@ -596,15 +720,29 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     if (this->hidden == NULL) {
     if (this->hidden == NULL) {
         return SDL_OutOfMemory();
         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 */
     /* 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)) {
     while ((!valid_datatype) && (test_format)) {
         this->spec.format = 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_F32LSB:
         case AUDIO_F32MSB:
         case AUDIO_F32MSB:
             valid_datatype = 1;
             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))
             if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
-                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
 
 
             if (SDL_AUDIO_ISFLOAT(this->spec.format))
             if (SDL_AUDIO_ISFLOAT(this->spec.format))
-                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
+                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
             else if (SDL_AUDIO_ISSIGNED(this->spec.format))
             else if (SDL_AUDIO_ISSIGNED(this->spec.format))
-                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
             break;
             break;
         }
         }
     }
     }
 
 
     if (!valid_datatype) {      /* shouldn't happen, but just in case... */
     if (!valid_datatype) {      /* shouldn't happen, but just in case... */
-        COREAUDIO_CloseDevice(this);
         return SDL_SetError("Unsupported audio format");
         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. */
+    }
+
+    this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
+    if (!this->hidden->thread) {
+        return -1;
     }
     }
 
 
-    return 0;   /* good to go. */
+    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
 static void
@@ -674,17 +831,11 @@ COREAUDIO_Init(SDL_AudioDriverImpl * impl)
     AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
     AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
 #else
 #else
     impl->OnlyHasDefaultOutputDevice = 1;
     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
 #endif
 
 
     impl->ProvidesOwnCallbackThread = 1;
     impl->ProvidesOwnCallbackThread = 1;
+    impl->HasCaptureSupport = 1;
 
 
     return 1;   /* this audio target is available. */
     return 1;   /* this audio target is available. */
 }
 }

+ 553 - 0
libs/SDL2/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
libs/SDL2/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: */

+ 138 - 0
libs/SDL2/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
libs/SDL2/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 */

BIN
libs/SDL2/src/main/winrt/SDL2-WinRTResource_BlankCursor.cur


+ 3 - 0
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"

+ 196 - 0
libs/SDL2/src/video/winrt/SDL_winrtgamebar.cpp

@@ -0,0 +1,196 @@
+/*
+  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_VIDEO_DRIVER_WINRT
+
+/* Windows includes */
+#include <roapi.h>
+#include <windows.foundation.h>
+#include <EventToken.h>
+
+
+/* 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<Windows::UI::Core::CoreDispatcher>();
+    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<Platform::Object ^> ^handler = \
+            ref new Windows::Foundation::EventHandler<Platform::Object ^>(&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: */

+ 13 - 3
libs/SDL2/src/audio/SDL_audiomem.h → libs/SDL2/src/video/winrt/SDL_winrtgamebar_cpp.h

@@ -18,8 +18,18 @@
      misrepresented as being the original software.
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
   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: */
 /* vi: set ts=4 sw=4 expandtab: */

+ 165 - 0
libs/SDL2/test/testaudiocapture.c

@@ -0,0 +1,165 @@
+/*
+  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.
+*/
+#include "SDL.h"
+
+#include <stdlib.h>
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#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;
+}
+

+ 216 - 0
libs/SDL2/test/testcustomcursor.c

@@ -0,0 +1,216 @@
+/*
+  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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#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);
+}