From 9cd2e9ec8fc0127393dfce9c0359d500c8c238be Mon Sep 17 00:00:00 2001
From: Edward Rudd <urkle@outoforder.cc>
Date: Tue, 09 Apr 2019 02:22:50 +0000
Subject: [PATCH] updae source to 2.0.9 source

---
 source/src/video/SDL_yuv.c                                                                  |   27 
 source/src/hidapi/LICENSE-gpl3.txt                                                          |  674 
 source/src/libm/math_libm.h                                                                 |    7 
 source/src/joystick/windows/SDL_mmjoystick.c                                                |   11 
 source/src/video/haiku/SDL_bwindow.cc                                                       |  196 
 source/src/hidapi/LICENSE.txt                                                               |   13 
 source/src/audio/SDL_audiodev_c.h                                                           |    6 
 source/src/video/windows/SDL_windowswindow.c                                                |   62 
 source/src/audio/pulseaudio/SDL_pulseaudio.c                                                |    2 
 source/src/video/windows/SDL_windowswindow.h                                                |    3 
 source/src/render/software/SDL_render_sw.c                                                  |   19 
 source/cmake/sdlchecks.cmake                                                                |   58 
 source/src/sensor/dummy/SDL_dummysensor.c                                                   |  110 
 source/src/audio/directsound/SDL_directsound.c                                              |    6 
 source/test/testresample.c                                                                  |    2 
 source/src/SDL_assert.c                                                                     |   16 
 source/src/sensor/dummy/SDL_dummysensor.h                                                   |   23 
 source/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj                                      |  161 
 source/android-project/app/jni/Application.mk                                               |    5 
 source/src/video/SDL_shape_internals.h                                                      |   12 
 source/src/video/x11/SDL_x11framebuffer.h                                                   |    6 
 source/src/joystick/emscripten/SDL_sysjoystick.c                                            |  130 
 source/include/SDL_config_os2.h                                                             |  170 
 source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters                       |   32 
 source/include/SDL.h                                                                        |    4 
 source/include/SDL_cpuinfo.h                                                                |   13 
 source/src/joystick/hidapi/SDL_hidapi_xboxone.c                                             |  324 
 source/src/video/emscripten/SDL_emscriptenmouse.c                                           |    1 
 source/src/thread/psp/SDL_systhread.c                                                       |    2 
 source/src/video/windows/SDL_windowstaskdialog.h                                            |  156 
 source/src/events/SDL_events.c                                                              |   23 
 source/include/SDL_config_psp.h                                                             |   17 
 source/src/video/windows/SDL_windowsevents.c                                                |   52 
 source/src/video/haiku/SDL_bclipboard.cc                                                    |   90 
 source/src/video/haiku/SDL_bopengl.h                                                        |   20 
 source/include/SDL_config.h.in                                                              |   11 
 source/src/joystick/iphoneos/SDL_sysjoystick.m                                              |  283 
 source/src/hidapi/testgui/testgui.vcproj                                                    |  217 
 source/src/joystick/steam/SDL_steamcontroller.h                                             |    6 
 source/src/hidapi/linux/Makefile-manual                                                     |   49 
 source/test/testjoystick.c                                                                  |    4 
 source/src/main/haiku/SDL_BApp.h                                                            |   10 
 source/include/SDL_hints.h                                                                  |  115 
 source/src/video/haiku/SDL_bmodes.cc                                                        |  406 
 source/include/SDL_vulkan.h                                                                 |   17 
 source/src/video/uikit/SDL_uikitvideo.m                                                     |   11 
 source/src/render/SDL_yuv_sw_c.h                                                            |    6 
 source/src/joystick/bsd/SDL_sysjoystick.c                                                   |  113 
 source/src/main/windows/version.rc                                                          |    8 
 source/src/render/SDL_sysrender.h                                                           |   10 
 source/src/video/uikit/SDL_uikitmodes.h                                                     |    4 
 source/src/hidapi/configure.ac                                                              |  236 
 source/src/hidapi/linux/Makefile.am                                                         |   10 
 source/src/hidapi/testgui/testgui.sln                                                       |   20 
 source/src/video/windows/SDL_windowsopengl.h                                                |   48 
 source/src/video/cocoa/SDL_cocoavulkan.m                                                    |   25 
 source/src/video/android/SDL_androidwindow.c                                                |   37 
 source/src/video/SDL_vulkan_utils.c                                                         |    8 
 source/src/hidapi/windows/ddk_build/makefile                                                |   49 
 source/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj                                 |   33 
 source/include/SDL_config_macosx.h                                                          |    6 
 source/include/SDL_stdinc.h                                                                 |   48 
 source/src/hidapi/testgui/TestGUI.app.in/Contents/Info.plist                                |   28 
 source/src/hidapi/windows/hidapi.sln                                                        |   29 
 source/wayland-protocols/relative-pointer-unstable-v1.xml                                   |  136 
 source/src/joystick/windows/SDL_dinputjoystick.c                                            |  541 
 source/src/video/uikit/SDL_uikitmodes.m                                                     |   56 
 source/src/video/haiku/SDL_bevents.cc                                                       |    4 
 source/include/SDL_config_android.h                                                         |    6 
 source/src/joystick/linux/SDL_sysjoystick_c.h                                               |    9 
 source/src/thread/SDL_thread.c                                                              |    6 
 source/src/video/SDL_blit_N.c                                                               |   53 
 source/src/events/SDL_mouse.c                                                               |   52 
 source/src/hidapi/linux/hidraw.cpp                                                          |    3 
 source/src/sensor/android/SDL_androidsensor.h                                               |   31 
 source/src/video/x11/SDL_x11keyboard.c                                                      |    8 
 source/test/testsensor.c                                                                    |  117 
 source/include/SDL_syswm.h                                                                  |   12 
 source/src/sensor/android/SDL_androidsensor.c                                               |  211 
 source/.hgtags                                                                              |    1 
 source/wayland-protocols/wayland.xml                                                        | 2746 +++
 source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns                   |    0 
 source/src/audio/wasapi/SDL_wasapi_winrt.cpp                                                |   65 
 source/src/video/cocoa/SDL_cocoaevents.m                                                    |    4 
 source/src/video/directfb/SDL_DirectFB_video.c                                              |    2 
 source/src/cpuinfo/SDL_cpuinfo.c                                                            |  105 
 source/src/timer/SDL_timer_c.h                                                              |    6 
 source/src/core/linux/SDL_udev.h                                                            |   32 
 source/src/hidapi/testgui/Makefile.mac                                                      |   46 
 source/src/video/haiku/SDL_bframebuffer.h                                                   |    8 
 source/src/hidapi/windows/hidtest.vcproj                                                    |  196 
 source/test/testplatform.c                                                                  |    1 
 source/src/hidapi/m4/ax_pthread.m4                                                          |  309 
 source/include/SDL_events.h                                                                 |   34 
 source/src/core/linux/SDL_udev.c                                                            |   80 
 source/src/audio/alsa/SDL_alsa_audio.c                                                      |  150 
 source/src/events/SDL_displayevents.c                                                       |   60 
 source/android-project/build.gradle                                                         |    4 
 source/src/events/SDL_displayevents_c.h                                                     |   30 
 source/src/render/direct3d/SDL_render_d3d.c                                                 |   14 
 source/src/video/SDL_egl.c                                                                  |   88 
 source/src/video/haiku/SDL_bkeyboard.cc                                                     |  260 
 source/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj                                          |  276 
 source/src/render/psp/SDL_render_psp.c                                                      |   14 
 source/src/power/SDL_syspower.h                                                             |    4 
 source/VisualC/SDL/SDL.vcxproj                                                              |   39 
 source/src/atomic/SDL_spinlock.c                                                            |   28 
 source/src/joystick/darwin/SDL_sysjoystick_c.h                                              |    6 
 source/src/thread/pthread/SDL_sysmutex.c                                                    |   11 
 source/android-project/gradle/wrapper/gradle-wrapper.properties                             |    2 
 source/src/hidapi/ios/Makefile-manual                                                       |   32 
 source/src/hidapi/bootstrap                                                                 |    2 
 source/include/SDL_audio.h                                                                  |    3 
 source/src/joystick/sort_controllers.py                                                     |   22 
 source/src/video/SDL_pixels.c                                                               |    2 
 source/src/hidapi/mac/hid.c                                                                 | 1191 +
 source/src/video/SDL_blit_slow.h                                                            |    6 
 source/src/video/wayland/SDL_waylandevents.c                                                |   19 
 source/src/video/cocoa/SDL_cocoavideo.m                                                     |    9 
 source/src/video/haiku/SDL_bkeyboard.h                                                      |    8 
 source/configure                                                                            |  349 
 source/src/video/cocoa/SDL_cocoavideo.h                                                     |    2 
 source/wayland-protocols/pointer-constraints-unstable-v1.xml                                |  339 
 source/VisualC/SDL/SDL.vcxproj.filters                                                      |  939 
 source/src/sensor/SDL_syssensor.h                                                           |  105 
 source/src/sensor/SDL_sensor_c.h                                                            |   44 
 source/src/video/windows/SDL_windowsvideo.c                                                 |   11 
 source/wayland-protocols/xdg-shell-unstable-v6.xml                                          | 1044 +
 source/src/hidapi/linux/hid.c                                                               |  898 +
 source/configure.in                                                                         |  235 
 source/src/audio/wasapi/SDL_wasapi_win32.c                                                  |   68 
 source/src/hidapi/android/project.properties                                                |   14 
 source/include/SDL_config.h.cmake                                                           |   12 
 source/test/testthread.c                                                                    |   32 
 source/wayland-protocols/xdg-shell.xml                                                      | 1120 +
 source/src/joystick/hidapi/SDL_hidapi_xbox360.c                                             |  787 +
 source/src/joystick/hidapi/SDL_hidapijoystick.c                                             | 1071 +
 source/src/video/haiku/SDL_bclipboard.h                                                     |    6 
 source/src/render/software/SDL_drawline.h                                                   |    6 
 source/src/hidapi/pc/hidapi-libusb.pc.in                                                    |   10 
 source/src/hidapi/testgui/start.sh                                                          |    2 
 source/src/events/SDL_events_c.h                                                            |    9 
 source/src/hidapi/doxygen/Doxyfile                                                          | 1630 ++
 source/src/hidapi/testgui/Makefile.am                                                       |   43 
 source/src/video/SDL_rect_c.h                                                               |    6 
 source/src/thread/pthread/SDL_systhread.c                                                   |  102 
 source/src/hidapi/windows/ddk_build/hidapi.def                                              |   17 
 source/src/libm/e_rem_pio2.c                                                                |    2 
 source/src/audio/SDL_audiocvt.c                                                             |    4 
 source/src/joystick/android/SDL_sysjoystick.c                                               |  347 
 source/src/audio/wasapi/SDL_wasapi.c                                                        |    6 
 source/src/hidapi/LICENSE-bsd.txt                                                           |   26 
 source/src/hidapi/testgui/Makefile.mingw                                                    |   32 
 source/src/core/linux/SDL_evdev.c                                                           |    1 
 source/Makefile.minimal                                                                     |    2 
 source/android-project/app/src/main/AndroidManifest.xml                                     |   18 
 source/src/video/x11/SDL_x11video.c                                                         |    1 
 source/src/video/cocoa/SDL_cocoamouse.m                                                     |   10 
 source/src/video/dummy/SDL_nullevents_c.h                                                   |    6 
 source/src/audio/jack/SDL_jackaudio.h                                                       |    1 
 source/include/SDL_config_iphoneos.h                                                        |   10 
 source/src/audio/jack/SDL_jackaudio.c                                                       |   35 
 source/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java           |  483 
 source/src/hidapi/testgui/test.cpp                                                          |  532 
 source/src/video/haiku/SDL_bvideo.cc                                                        |  147 
 source/src/joystick/hidapi/SDL_hidapi_switch.c                                              |  905 +
 source/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java                      |   19 
 source/src/cpuinfo/SDL_simd.h                                                               |   88 
 source/src/joystick/windows/SDL_dinputjoystick_c.h                                          |    1 
 source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj                               |    8 
 source/src/render/software/SDL_blendline.h                                                  |    6 
 source/test/testvulkan.c                                                                    |    4 
 source/src/video/haiku/SDL_bopengl.cc                                                       |   20 
 source/src/video/cocoa/SDL_cocoametalview.h                                                 |    8 
 source/src/video/x11/SDL_x11window.c                                                        |   22 
 source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters                             |   24 
 source/src/video/cocoa/SDL_cocoametalview.m                                                 |   63 
 source/src/hidapi/testgui/Makefile-manual                                                   |   26 
 source/Makefile.in                                                                          |    3 
 source/src/video/cocoa/SDL_cocoaopengl.m                                                    |   16 
 source/src/core/winrt/SDL_winrtapp_xaml.cpp                                                 |    2 
 source/src/hidapi/ios/Makefile.am                                                           |    9 
 source/include/SDL_version.h                                                                |    2 
 source/test/testcustomcursor.c                                                              |   55 
 source/src/video/x11/SDL_x11window.h                                                        |    1 
 source/src/video/haiku/SDL_bwindow.h                                                        |   40 
 source/src/hidapi/linux/hid.cpp                                                             |  333 
 source/src/video/SDL_blit_1.c                                                               |  220 
 source/src/stdlib/SDL_string.c                                                              |  132 
 source/src/SDL.c                                                                            |   48 
 source/src/video/SDL_video.c                                                                |  110 
 source/src/audio/arts/SDL_artsaudio.c                                                       |    2 
 source/src/audio/winmm/SDL_winmm.c                                                          |    5 
 source/src/video/x11/SDL_x11messagebox.h                                                    |    5 
 source/src/haptic/linux/SDL_syshaptic.c                                                     |   12 
 source/src/video/x11/SDL_x11messagebox.c                                                    |   41 
 source/src/render/opengl/SDL_shaders_gl.h                                                   |    6 
 source/src/sensor/coremotion/SDL_coremotionsensor.m                                         |  234 
 source/src/core/android/SDL_android.h                                                       |   29 
 source/src/sensor/coremotion/SDL_coremotionsensor.h                                         |   30 
 source/src/events/SDL_windowevents.c                                                        |   25 
 source/src/hidapi/windows/Makefile.mingw                                                    |   35 
 source/src/video/windows/SDL_windowsvulkan.c                                                |    2 
 source/src/video/SDL_blit_A.c                                                               |  998 
 source/src/joystick/hidapi/SDL_hidapijoystick_c.h                                           |   74 
 source/src/hidapi/hidtest/hidtest.cpp                                                       |  194 
 source/src/hidapi/windows/Makefile-manual                                                   |   14 
 source/src/dynapi/SDL_dynapi_procs.h                                                        |   48 
 source/src/haptic/android/SDL_syshaptic.c                                                   |   13 
 source/src/core/android/SDL_android.c                                                       |  430 
 source/build-scripts/mkinstalldirs                                                          |  173 
 source/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java                |  308 
 source/src/hidapi/android/hid.cpp                                                           | 1159 +
 source/src/render/metal/SDL_render_metal.m                                                  |   44 
 source/src/haptic/SDL_haptic_c.h                                                            |    5 
 source/src/joystick/SDL_gamecontrollerdb.h                                                  |  450 
 source/include/SDL_video.h                                                                  |   51 
 source/src/video/emscripten/SDL_emscriptenopengles.c                                        |    4 
 source/build-scripts/ltmain.sh                                                              |    7 
 source/src/joystick/windows/SDL_windowsjoystick.c                                           |  129 
 source/src/hidapi/m4/pkg.m4                                                                 |  157 
 source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java                   |  307 
 source/src/sensor/SDL_sensor.c                                                              |  546 
 source/build-scripts/winrtbuild.ps1                                                         |    2 
 source/src/atomic/SDL_atomic.c                                                              |    3 
 source/src/video/kmsdrm/SDL_kmsdrmopengles.c                                                |   24 
 source/include/SDL_config_wiz.h                                                             |    4 
 source/src/hidapi/ios/hid.m                                                                 |  914 +
 source/test/testgamecontroller.c                                                            |    5 
 source/include/SDL_gamecontroller.h                                                         |   28 
 source/src/video/x11/SDL_x11opengl.h                                                        |   16 
 source/src/hidapi/README.txt                                                                |  339 
 source/src/dynapi/SDL_dynapi.c                                                              |   35 
 source/src/events/scancodes_xfree86.h                                                       |    6 
 source/src/libm/e_exp.c                                                                     |  187 
 source/Android.mk                                                                           |   25 
 source/Xcode-iOS/Demos/Demos.xcodeproj/project.pbxproj                                      |    2 
 source/include/SDL_joystick.h                                                               |   34 
 source/src/haptic/windows/SDL_windowshaptic.c                                               |   15 
 source/src/hidapi/windows/hidapi.vcproj                                                     |  201 
 source/src/render/opengles2/SDL_render_gles2.c                                              |   41 
 source/src/power/linux/SDL_syspower.c                                                       |    6 
 source/src/render/opengles/SDL_render_gles.c                                                |   14 
 source/src/video/raspberry/SDL_rpivideo.c                                                   |   18 
 source/src/render/software/SDL_blendpoint.h                                                 |    6 
 source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java    |  642 
 source/src/video/SDL_blit.h                                                                 |   20 
 source/src/hidapi/libusb/hidusb.cpp                                                         |    3 
 source/Makefile.os2                                                                         |  126 
 source/Makefile.psp                                                                         |    2 
 source/src/hidapi/mac/Makefile-manual                                                       |   32 
 source/src/hidapi/libusb/Makefile.linux                                                     |   49 
 source/src/hidapi/testgui/mac_support.cpp                                                   |  134 
 source/src/video/uikit/SDL_uikitview.m                                                      |   36 
 source/src/hidapi/android/jni/Android.mk                                                    |   16 
 source/src/joystick/android/SDL_sysjoystick_c.h                                             |    6 
 source/src/joystick/dummy/SDL_sysjoystick.c                                                 |  111 
 source/src/video/SDL_surface.c                                                              |   16 
 source/src/video/x11/SDL_x11sym.h                                                           |    6 
 source/src/core/linux/SDL_dbus.c                                                            |   18 
 source/src/joystick/SDL_gamecontroller.c                                                    |  322 
 source/src/video/haiku/SDL_bvideo.h                                                         |    8 
 source/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj                                |   86 
 source/src/audio/android/SDL_androidaudio.c                                                 |   25 
 source/src/core/linux/SDL_dbus.h                                                            |    9 
 source/src/video/yuv2rgb/yuv_rgb_std_func.h                                                 |   58 
 source/src/main/windows/SDL_windows_main.c                                                  |   82 
 source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters                                     |   24 
 source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj                                             |    8 
 source/build-scripts/install-sh                                                             |  532 
 source/src/hidapi/testgui/mac_support.h                                                     |   17 
 source/src/hidapi/android/jni/Application.mk                                                |    2 
 source/test/testwm2.c                                                                       |    3 
 source/src/joystick/haiku/SDL_haikujoystick.cc                                              |   90 
 source/src/render/opengles2/SDL_shaders_gles2.c                                             |   14 
 source/src/audio/SDL_sysaudio.h                                                             |    4 
 source/include/SDL_thread.h                                                                 |   41 
 source/src/video/SDL_pixels_c.h                                                             |    6 
 source/src/video/android/SDL_androidvideo.c                                                 |   33 
 source/src/hidapi/AUTHORS.txt                                                               |   16 
 source/src/video/android/SDL_androidvideo.h                                                 |    8 
 source/src/haptic/SDL_haptic.c                                                              |    7 
 source/src/core/linux/SDL_evdev_kbd.c                                                       |  171 
 source/src/joystick/windows/SDL_windowsjoystick_c.h                                         |    8 
 source/src/test/SDL_test_common.c                                                           |   45 
 source/src/core/linux/SDL_evdev_kbd.h                                                       |    5 
 source/src/video/SDL_sysvideo.h                                                             |   22 
 source/src/video/SDL_yuv_c.h                                                                |    6 
 source/src/hidapi/mac/Makefile.am                                                           |    9 
 source/src/main/haiku/SDL_BeApp.cc                                                          |   48 
 source/include/SDL_surface.h                                                                |    7 
 source/src/render/software/SDL_blendfillrect.h                                              |    6 
 source/src/hidapi/linux/README.txt                                                          |   59 
 source/include/SDL_config_pandora.h                                                         |    3 
 source/src/joystick/psp/SDL_sysjoystick.c                                                   |    6 
 source/src/hidapi/udev/99-hid.rules                                                         |   33 
 source/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java                    |  542 
 source/src/video/cocoa/SDL_cocoamousetap.m                                                  |    9 
 source/src/video/wayland/SDL_waylandtouch.h                                                 |   10 
 source/src/video/haiku/SDL_bmodes.h                                                         |   14 
 source/.hgignore                                                                            |    1 
 source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java               |  682 
 source/src/hidapi/libusb/Makefile.freebsd                                                   |   46 
 source/src/joystick/iphoneos/SDL_sysjoystick_c.h                                            |    4 
 source/src/video/wayland/SDL_waylandtouch.c                                                 |    6 
 source/src/hidapi/testgui/Makefile.freebsd                                                  |   33 
 source/docs/README-raspberrypi.md                                                           |   10 
 source/src/hidapi/windows/Makefile.am                                                       |   16 
 source/src/hidapi/pc/hidapi-hidraw.pc.in                                                    |   10 
 source/src/video/haiku/SDL_BWin.h                                                           |    4 
 source/src/video/SDL_RLEaccel_c.h                                                           |    7 
 source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj                                     |    8 
 source/src/hidapi/testgui/mac_support_cocoa.m                                               |   94 
 source/android-project/app/build.gradle                                                     |   13 
 source/include/SDL_haptic.h                                                                 |   17 
 source/src/render/opengl/SDL_render_gl.c                                                    |   14 
 source/src/test/SDL_test_harness.c                                                          |    3 
 source/src/video/wayland/SDL_waylandwindow.h                                                |   12 
 source/src/video/kmsdrm/SDL_kmsdrmvideo.c                                                   |   87 
 source/src/video/kmsdrm/SDL_kmsdrmvideo.h                                                   |    1 
 source/src/video/wayland/SDL_waylandwindow.c                                                |  176 
 source/src/hidapi/windows/hid.c                                                             |  988 +
 source/src/joystick/controller_type.h                                                       |  427 
 source/src/video/cocoa/SDL_cocoawindow.m                                                    |   38 
 source/src/video/cocoa/SDL_cocoawindow.h                                                    |    1 
 source/src/audio/SDL_audiotypecvt.c                                                         |  736 
 source/src/render/SDL_render.c                                                              |  119 
 source/src/hidapi/testgui/Makefile.linux                                                    |   32 
 source/src/joystick/SDL_joystick_c.h                                                        |   46 
 source/CMakeLists.txt                                                                       |   96 
 source/src/hidapi/hidapi/hidapi.h                                                           |  398 
 source/src/hidapi/libusb/Makefile-manual                                                    |   18 
 source/src/core/windows/SDL_windows.c                                                       |    3 
 source/Xcode/SDL/SDL.xcodeproj/project.pbxproj                                              |  377 
 source/src/audio/SDL_audio.c                                                                |  111 
 source/include/SDL_config_minimal.h                                                         |    3 
 source/src/hidapi/windows/ddk_build/sources                                                 |   23 
 source/src/video/dummy/SDL_nullframebuffer_c.h                                              |    6 
 source/src/render/software/SDL_rotate.h                                                     |    4 
 source/src/render/software/SDL_rotate.c                                                     |   10 
 source/src/power/SDL_power.c                                                                |   12 
 source/VisualC/SDLmain/SDLmain.vcxproj                                                      |    2 
 source/Makefile.wiz                                                                         |   35 
 source/src/libm/math_private.h                                                              |    7 
 source/include/SDL_config_windows.h                                                         |    9 
 source/src/video/uikit/SDL_uikitappdelegate.m                                               |   25 
 source/src/stdlib/SDL_stdlib.c                                                              |   24 
 source/src/video/windows/SDL_windowsmouse.c                                                 |    2 
 source/src/hidapi/Makefile.am                                                               |   85 
 source/src/hidapi/libusb/hid.c                                                              | 1620 ++
 source/src/video/haiku/SDL_bframebuffer.cc                                                  |  314 
 source/src/SDL_assert_c.h                                                                   |    5 
 source/src/video/uikit/SDL_uikitmetalview.m                                                 |   18 
 source/Xcode/SDL/Info-Framework.plist                                                       |    4 
 source/src/render/software/SDL_render_sw_c.h                                                |    5 
 source/src/render/direct3d11/SDL_render_d3d11.c                                             |   17 
 source/src/events/SDL_mouse_c.h                                                             |    5 
 source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings |    0 
 source/src/joystick/windows/SDL_xinputjoystick_c.h                                          |    1 
 source/src/render/software/SDL_drawpoint.h                                                  |    6 
 source/src/joystick/SDL_sysjoystick.h                                                       |  119 
 source/src/audio/coreaudio/SDL_coreaudio.m                                                  |  224 
 source/include/SDL_sensor.h                                                                 |  251 
 source/src/video/kmsdrm/SDL_kmsdrmmouse.c                                                   |   10 
 source/src/joystick/darwin/SDL_sysjoystick.c                                                |  354 
 source/src/video/uikit/SDL_uikitmessagebox.m                                                |    2 
 source/acinclude/ax_gcc_x86_cpuid.m4                                                        |    0 
 source/src/audio/coreaudio/SDL_coreaudio.h                                                  |    8 
 source/src/video/windows/SDL_windowsmessagebox.c                                            |  469 
 source/src/video/SDL_blit_copy.h                                                            |    5 
 source/docs/README-android.md                                                               |   39 
 source/WhatsNew.txt                                                                         |   41 
 source/src/dynapi/SDL_dynapi_overrides.h                                                    |   32 
 source/src/hidapi/testgui/copy_to_bundle.sh                                                 |   97 
 source/test/Makefile.in                                                                     |   12 
 source/src/joystick/linux/SDL_sysjoystick.c                                                 |  156 
 source/src/hidapi/LICENSE-orig.txt                                                          |    9 
 source/src/hidapi/libusb/Makefile.am                                                        |   27 
 source/src/video/SDL_egl_c.h                                                                |    1 
 source/src/thread/windows/SDL_systhread.c                                                   |    2 
 source/src/hidapi/testgui/TestGUI.app.in/Contents/PkgInfo                                   |    1 
 source/src/hidapi/pc/hidapi.pc.in                                                           |   10 
 source/src/video/uikit/SDL_uikitopenglview.m                                                |    2 
 source/src/video/uikit/SDL_uikitviewcontroller.m                                            |  109 
 source/include/SDL_system.h                                                                 |   32 
 source/android-project/app/src/main/java/org/libsdl/app/SDL.java                            |   47 
 source/include/SDL_config_winrt.h                                                           |   10 
 source/include/SDL_config.h                                                                 |    4 
 source/src/joystick/hidapi/SDL_hidapi_ps4.c                                                 |  566 
 source/src/video/wayland/SDL_waylandvideo.c                                                 |   26 
 source/src/video/haiku/SDL_bevents.h                                                        |    2 
 source/src/video/wayland/SDL_waylandvideo.h                                                 |   12 
 source/src/joystick/SDL_joystick.c                                                          |  466 
 source/src/video/android/SDL_androidmouse.h                                                 |    3 
 source/Makefile.pandora                                                                     |   41 
 source/src/joystick/windows/SDL_xinputjoystick.c                                            |  104 
 source/src/stdlib/SDL_iconv.c                                                               |   16 
 source/src/video/android/SDL_androidmouse.c                                                 |  157 
 /dev/null                                                                                   |  355 
 source/src/hidapi/HACKING.txt                                                               |   15 
 source/src/hidapi/hidtest/Makefile.am                                                       |   20 
 source/debian/changelog                                                                     |    6 
 source/src/libm/k_rem_pio2.c                                                                |   22 
 403 files changed, 40,159 insertions(+), 5,867 deletions(-)

diff --git a/source/.hgignore b/source/.hgignore
index d4c66b8..d839b71 100644
--- a/source/.hgignore
+++ b/source/.hgignore
@@ -112,6 +112,7 @@
 test/testrumble
 test/testscale
 test/testsem
+test/testsensor
 test/testshader
 test/testshape
 test/testsprite2
diff --git a/source/.hgtags b/source/.hgtags
index c23d0f0..661b33b 100644
--- a/source/.hgtags
+++ b/source/.hgtags
@@ -32,3 +32,4 @@
 007dfe83abf81b1ff5df40186f65e8e64987b825 release-2.0.5
 8df7a59b55283aa09889522369a2b32674c048de release-2.0.6
 2088cd828335797d73d151e3288d899f77204862 release-2.0.7
+f1084c419f33610cf274e309a8b2798d2ae665c7 release-2.0.8
diff --git a/source/Android.mk b/source/Android.mk
old mode 100755
new mode 100644
index d56b5c0..9ce879a
--- a/source/Android.mk
+++ b/source/Android.mk
@@ -31,11 +31,13 @@
 	$(wildcard $(LOCAL_PATH)/src/haptic/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/joystick/android/*.c) \
-	$(LOCAL_PATH)/src/joystick/steam/SDL_steamcontroller.c \
+	$(wildcard $(LOCAL_PATH)/src/joystick/hidapi/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/filesystem/android/*.c) \
+	$(wildcard $(LOCAL_PATH)/src/sensor/*.c) \
+	$(wildcard $(LOCAL_PATH)/src/sensor/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/render/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/render/*/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/stdlib/*.c) \
@@ -48,10 +50,14 @@
 	$(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/test/*.c))
 
+LOCAL_SHARED_LIBRARIES := hidapi
+
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
 LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog -landroid
 
-cmd-strip := 
+ifeq ($(NDK_DEBUG),1)
+    cmd-strip :=
+endif
 
 include $(BUILD_SHARED_LIBRARY)
 
@@ -86,4 +92,19 @@
 
 include $(BUILD_STATIC_LIBRARY)
 
+###########################
+#
+# hidapi library
+#
+###########################
 
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS += -std=c++11
+
+LOCAL_SRC_FILES := src/hidapi/android/hid.cpp
+
+LOCAL_MODULE := libhidapi
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 8f1e828..0128c7a 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -42,10 +42,13 @@
 # set SDL_BINARY_AGE and SDL_INTERFACE_AGE to 0.
 set(SDL_MAJOR_VERSION 2)
 set(SDL_MINOR_VERSION 0)
-set(SDL_MICRO_VERSION 8)
+set(SDL_MICRO_VERSION 9)
 set(SDL_INTERFACE_AGE 0)
-set(SDL_BINARY_AGE 8)
+set(SDL_BINARY_AGE 9)
 set(SDL_VERSION "${SDL_MAJOR_VERSION}.${SDL_MINOR_VERSION}.${SDL_MICRO_VERSION}")
+# the following should match the versions in Xcode project file:
+set(DYLIB_CURRENT_VERSION 10.0.0)
+set(DYLIB_COMPATIBILITY_VERSION 1.0.0)
 
 # Set defaults preventing destination file conflicts
 set(SDL_CMAKE_DEBUG_POSTFIX "d"
@@ -210,8 +213,14 @@
 set(SDL_LIBS "-lSDL2")
 set(SDL_CFLAGS "")
 
-# Emscripten toolchain has a nonempty default value for this, and the checks 
-# in this file need to change that, so remember the original value, and 
+# When building shared lib for Windows with MinGW,
+# avoid the DLL having a "lib" prefix
+if(WINDOWS)
+  set(CMAKE_SHARED_LIBRARY_PREFIX "")
+endif()
+
+# Emscripten toolchain has a nonempty default value for this, and the checks
+# in this file need to change that, so remember the original value, and
 # restore back to that afterwards. For check_function_exists() to work in
 # Emscripten, this value must be at its default value.
 set(ORIG_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
@@ -244,7 +253,7 @@
 set(OPT_DEF_ASM TRUE)
 if(EMSCRIPTEN)
   # Set up default values for the currently supported set of subsystems:
-  # Emscripten/Javascript does not have assembly support, a dynamic library 
+  # Emscripten/Javascript does not have assembly support, a dynamic library
   # loading architecture, low-level CPU inspection or multithreading.
   set(OPT_DEF_ASM FALSE)
   set(SDL_SHARED_ENABLED_BY_DEFAULT OFF)
@@ -261,7 +270,7 @@
 
 set(SDL_SUBSYSTEMS
     Atomic Audio Video Render Events Joystick Haptic Power Threads Timers
-    File Loadso CPUinfo Filesystem Dlopen)
+    File Loadso CPUinfo Filesystem Dlopen Sensor)
 foreach(_SUB ${SDL_SUBSYSTEMS})
   string(TOUPPER ${_SUB} _OPT)
   if (NOT DEFINED SDL_${_OPT}_ENABLED_BY_DEFAULT)
@@ -328,6 +337,7 @@
 endforeach()
 set_option(VIDEO_COCOA         "Use Cocoa video driver" ${APPLE})
 set_option(DIRECTX             "Use DirectX for Windows audio/video" ${WINDOWS})
+set_option(WASAPI              "Use the Windows WASAPI audio driver" ${WINDOWS})
 set_option(RENDER_D3D          "Enable the Direct3D render driver" ${WINDOWS})
 set_option(VIDEO_VIVANTE       "Use Vivante EGL video driver" ${UNIX_SYS})
 dep_option(VIDEO_VULKAN        "Enable Vulkan support" ON "ANDROID OR APPLE OR LINUX OR WINDOWS" OFF)
@@ -444,6 +454,8 @@
 
   if(APPLE)
     list(APPEND EXTRA_LDFLAGS "-Wl,-undefined,error")
+    list(APPEND EXTRA_LDFLAGS "-Wl,-compatibility_version,${DYLIB_COMPATIBILITY_VERSION}")
+    list(APPEND EXTRA_LDFLAGS "-Wl,-current_version,${DYLIB_CURRENT_VERSION}")
   else()
     set(CMAKE_REQUIRED_FLAGS "-Wl,--no-undefined")
     check_c_compiler_flag("" HAVE_NO_UNDEFINED)
@@ -643,7 +655,7 @@
             _ultoa strtol strtoul strtoll strtod atoi atof strcmp strncmp
             _stricmp _strnicmp sscanf
             acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf
-            copysign copysignf cos cosf fabs fabsf floor floorf fmod fmodf
+            copysign copysignf cos cosf exp expf fabs fabsf floor floorf fmod fmodf
             log logf log10 log10f pow powf scalbn scalbnf sin sinf sqrt sqrtf tan tanf)
       string(TOUPPER ${_FN} _UPPER)
       set(HAVE_${_UPPER} 1)
@@ -742,6 +754,10 @@
   endif()
   file(GLOB HAPTIC_SOURCES ${SDL2_SOURCE_DIR}/src/haptic/*.c)
   set(SOURCE_FILES ${SOURCE_FILES} ${HAPTIC_SOURCES})
+endif()
+if(SDL_SENSOR)
+  file(GLOB SENSOR_SOURCES ${SDL2_SOURCE_DIR}/src/sensor/*.c)
+  set(SOURCE_FILES ${SOURCE_FILES} ${SENSOR_SOURCES})
 endif()
 if(SDL_POWER)
   file(GLOB POWER_SOURCES ${SDL2_SOURCE_DIR}/src/power/*.c)
@@ -843,6 +859,12 @@
     file(GLOB TIMER_SOURCES ${SDL2_SOURCE_DIR}/src/timer/unix/*.c)
     set(SOURCE_FILES ${SOURCE_FILES} ${TIMER_SOURCES})
     set(HAVE_SDL_TIMERS TRUE)
+  endif()
+  if(SDL_SENSOR)
+    set(SDL_SENSOR_ANDROID 1)
+    set(HAVE_SDL_SENSORS TRUE)
+    file(GLOB ANDROID_SENSOR_SOURCES ${SDL2_SOURCE_DIR}/src/sensor/android/*.c)
+    set(SOURCE_FILES ${SOURCE_FILES} ${ANDROID_SENSOR_SOURCES})
   endif()
   if(SDL_VIDEO)
     set(SDL_VIDEO_DRIVER_ANDROID 1)
@@ -1001,7 +1023,7 @@
         #include <linux/kd.h>
         #include <linux/keyboard.h>
 
-        int main(int argc, char **argv) 
+        int main(int argc, char **argv)
         {
             struct kbentry kbe;
             kbe.kb_table = KG_CTRL;
@@ -1041,6 +1063,12 @@
         set(HAVE_IBUS_IBUS_H TRUE)
         include_directories(${IBUS_INCLUDE_DIRS})
         list(APPEND EXTRA_LIBS ${IBUS_LIBRARIES})
+      endif()
+      if(HAVE_LIBUNWIND_H)
+        # We've already found the header, so REQUIRE the lib to be present
+        pkg_search_module(UNWIND REQUIRED libunwind)
+        pkg_search_module(UNWIND_GENERIC REQUIRED libunwind-generic)
+        list(APPEND EXTRA_LIBS ${UNWIND_LIBRARIES} ${UNWIND_GENERIC_LIBRARIES})
       endif()
     endif()
 
@@ -1183,8 +1211,6 @@
     check_include_file(ddraw.h HAVE_DDRAW_H)
     check_include_file(dsound.h HAVE_DSOUND_H)
     check_include_file(dinput.h HAVE_DINPUT_H)
-    check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H)
-    check_include_file(audioclient.h HAVE_AUDIOCLIENT_H)
     check_include_file(dxgi.h HAVE_DXGI_H)
     if(HAVE_D3D_H OR HAVE_D3D11_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H)
       set(HAVE_DIRECTX TRUE)
@@ -1196,6 +1222,11 @@
     endif()
     set(CMAKE_REQUIRED_FLAGS ${ORIG_CMAKE_REQUIRED_FLAGS})
   endif()
+
+  # headers needed elsewhere ...
+  check_include_file(mmdeviceapi.h HAVE_MMDEVICEAPI_H)
+  check_include_file(audioclient.h HAVE_AUDIOCLIENT_H)
+  check_include_file(endpointvolume.h HAVE_ENDPOINTVOLUME_H)
 
   if(SDL_AUDIO)
     set(SDL_AUDIO_DRIVER_WINMM 1)
@@ -1209,7 +1240,7 @@
       set(SOURCE_FILES ${SOURCE_FILES} ${DSOUND_AUDIO_SOURCES})
     endif()
 
-    if(HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H)
+    if(WASAPI AND HAVE_AUDIOCLIENT_H AND HAVE_MMDEVICEAPI_H)
       set(SDL_AUDIO_DRIVER_WASAPI 1)
       file(GLOB WASAPI_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/wasapi/*.c)
       set(SOURCE_FILES ${SOURCE_FILES} ${WASAPI_AUDIO_SOURCES})
@@ -1261,7 +1292,7 @@
   endif()
 
   # Libraries for Win32 native and MinGW
-  list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid)
+  list(APPEND EXTRA_LIBS user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 shell32)
 
   # TODO: in configure.in the check for timers is set on
   # cygwin | mingw32* - does this include mingw32CE?
@@ -1527,6 +1558,7 @@
 
 if(VIDEO_VULKAN)
   set(SDL_VIDEO_VULKAN 1)
+  set(HAVE_VIDEO_VULKAN TRUE)
 endif()
 
 # Dummies
@@ -1538,7 +1570,7 @@
 # This leads to missing internal references on building, since the
 # src/X/*.c does not get included.
 if(NOT HAVE_SDL_JOYSTICK)
-  set(SDL_JOYSTICK_DISABLED 1)
+  set(SDL_JOYSTICK_DUMMY 1)
   if(SDL_JOYSTICK AND NOT APPLE) # results in unresolved symbols on OSX
 
     file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/dummy/*.c)
@@ -1546,9 +1578,14 @@
   endif()
 endif()
 if(NOT HAVE_SDL_HAPTIC)
-  set(SDL_HAPTIC_DISABLED 1)
+  set(SDL_HAPTIC_DUMMY 1)
   file(GLOB HAPTIC_SOURCES ${SDL2_SOURCE_DIR}/src/haptic/dummy/*.c)
   set(SOURCE_FILES ${SOURCE_FILES} ${HAPTIC_SOURCES})
+endif()
+if(NOT HAVE_SDL_SENSORS)
+  set(SDL_SENSOR_DUMMY 1)
+  file(GLOB SENSORS_SOURCES ${SDL2_SOURCE_DIR}/src/sensor/dummy/*.c)
+  set(SOURCE_FILES ${SOURCE_FILES} ${SENSORS_SOURCES})
 endif()
 if(NOT HAVE_SDL_LOADSO)
   set(SDL_LOADSO_DISABLED 1)
@@ -1695,7 +1732,7 @@
 
 # Always build SDLmain
 add_library(SDL2main STATIC ${SDLMAIN_SOURCES})
-target_include_directories(SDL2main PUBLIC $<INSTALL_INTERFACE:include>)
+target_include_directories(SDL2main PUBLIC "$<BUILD_INTERFACE:${SDL2_SOURCE_DIR}/include>" $<INSTALL_INTERFACE:include/SDL2>)
 set(_INSTALL_LIBS "SDL2main")
 if (NOT ANDROID)
   set_target_properties(SDL2main PROPERTIES DEBUG_POSTFIX ${SDL_CMAKE_DEBUG_POSTFIX})
@@ -1704,7 +1741,9 @@
 if(SDL_SHARED)
   add_library(SDL2 SHARED ${SOURCE_FILES} ${VERSION_SOURCES})
   if(APPLE)
-    set_target_properties(SDL2 PROPERTIES MACOSX_RPATH 1)
+    set_target_properties(SDL2 PROPERTIES
+      MACOSX_RPATH 1
+      OUTPUT_NAME "SDL2-${LT_RELEASE}")
   elseif(UNIX AND NOT ANDROID)
     set_target_properties(SDL2 PROPERTIES
       VERSION ${LT_VERSION}
@@ -1724,7 +1763,7 @@
   endif()
   set(_INSTALL_LIBS "SDL2" ${_INSTALL_LIBS})
   target_link_libraries(SDL2 ${EXTRA_LIBS} ${EXTRA_LDFLAGS})
-  target_include_directories(SDL2 PUBLIC $<INSTALL_INTERFACE:include>)
+  target_include_directories(SDL2 PUBLIC "$<BUILD_INTERFACE:${SDL2_SOURCE_DIR}/include>" $<INSTALL_INTERFACE:include/SDL2>)
   if (NOT ANDROID)
     set_target_properties(SDL2 PROPERTIES DEBUG_POSTFIX ${SDL_CMAKE_DEBUG_POSTFIX})
   endif()
@@ -1750,7 +1789,7 @@
   # libraries - do we need to consider this?
   set(_INSTALL_LIBS "SDL2-static" ${_INSTALL_LIBS})
   target_link_libraries(SDL2-static ${EXTRA_LIBS} ${EXTRA_LDFLAGS})
-  target_include_directories(SDL2-static PUBLIC $<INSTALL_INTERFACE:include>)
+  target_include_directories(SDL2-static PUBLIC "$<BUILD_INTERFACE:${SDL2_SOURCE_DIR}/include>" $<INSTALL_INTERFACE:include/SDL2>)
   if (NOT ANDROID)
     set_target_properties(SDL2-static PROPERTIES DEBUG_POSTFIX ${SDL_CMAKE_DEBUG_POSTFIX})
   endif()
@@ -1761,7 +1800,7 @@
 if(SDL_TEST)
   file(GLOB TEST_SOURCES ${SDL2_SOURCE_DIR}/src/test/*.c)
   add_library(SDL2_test STATIC ${TEST_SOURCES})
-  
+
   add_subdirectory(test)
 endif()
 
@@ -1808,18 +1847,23 @@
 list(APPEND INCLUDE_FILES ${BIN_INCLUDE_FILES})
 install(FILES ${INCLUDE_FILES} DESTINATION include/SDL2)
 
+string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPER_BUILD_TYPE)
+if (UPPER_BUILD_TYPE MATCHES DEBUG)
+  set(SOPOSTFIX "${SDL_CMAKE_DEBUG_POSTFIX}")
+else()
+  set(SOPOSTFIX "")
+endif()
+
 if(NOT (WINDOWS OR CYGWIN))
   if(SDL_SHARED)
-    if (APPLE)
-        set(SOEXT "dylib")
-    else()
-        set(SOEXT "so")
-    endif()
+    set(SOEXT ${CMAKE_SHARED_LIBRARY_SUFFIX}) # ".so", ".dylib", etc.
+    get_target_property(SONAME SDL2 OUTPUT_NAME)
     if(NOT ANDROID)
         install(CODE "
           execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
-          \"libSDL2-2.0.${SOEXT}\" \"libSDL2.${SOEXT}\")")
-        install(FILES ${SDL2_BINARY_DIR}/libSDL2.${SOEXT} DESTINATION "lib${LIB_SUFFIX}")
+            \"lib${SONAME}${SOPOSTFIX}${SOEXT}\" \"libSDL2${SOPOSTFIX}${SOEXT}\")"
+            WORKING_DIR "${SDL2_BINARY_DIR}")
+        install(FILES ${SDL2_BINARY_DIR}/libSDL2${SOPOSTFIX}${SOEXT} DESTINATION "lib${LIB_SUFFIX}")
     endif()
   endif()
   if(FREEBSD)
diff --git a/source/Makefile.in b/source/Makefile.in
index fe56652..4eb6e63 100644
--- a/source/Makefile.in
+++ b/source/Makefile.in
@@ -44,7 +44,7 @@
 
 WAYLAND_SCANNER = @WAYLAND_SCANNER@
 
-SRC_DIST = *.txt acinclude Android.mk autogen.sh android-project build-scripts cmake cmake_uninstall.cmake.in configure configure.in debian docs include Makefile.* sdl2-config.cmake.in sdl2-config.in sdl2.m4 sdl2.pc.in SDL2.spec.in SDL2Config.cmake src test VisualC.html VisualC VisualC-WinRT Xcode Xcode-iOS
+SRC_DIST = *.txt acinclude Android.mk autogen.sh android-project build-scripts cmake cmake_uninstall.cmake.in configure configure.in debian docs include Makefile.* sdl2-config.cmake.in sdl2-config.in sdl2.m4 sdl2.pc.in SDL2.spec.in SDL2Config.cmake src test VisualC.html VisualC VisualC-WinRT Xcode Xcode-iOS wayland-protocols
 GEN_DIST = SDL2.spec
 
 ifneq ($V,1)
@@ -101,6 +101,7 @@
 	SDL_render.h \
 	SDL_rwops.h \
 	SDL_scancode.h \
+	SDL_sensor.h \
 	SDL_shape.h \
 	SDL_stdinc.h \
 	SDL_surface.h \
diff --git a/source/Makefile.minimal b/source/Makefile.minimal
index 6ec1ce8..7f02649 100644
--- a/source/Makefile.minimal
+++ b/source/Makefile.minimal
@@ -22,6 +22,8 @@
 	src/filesystem/dummy/*.c \
 	src/render/*.c \
 	src/render/software/*.c \
+	src/sensor/*.c \
+	src/sensor/dummy/*.c \
 	src/stdlib/*.c \
 	src/thread/*.c \
 	src/thread/generic/*.c \
diff --git a/source/Makefile.os2 b/source/Makefile.os2
new file mode 100644
index 0000000..95c5cef
--- /dev/null
+++ b/source/Makefile.os2
@@ -0,0 +1,126 @@
+# Open Watcom makefile to build SDL2.dll for OS/2:
+# wmake -f Makefile.os2
+
+LIBNAME = SDL2
+VERSION = 2.0.9
+DESCRIPTION = Simple DirectMedia Layer 2
+
+LIBHOME = .
+LIBPATH = $(LIBHOME)/lib
+DLLFILE = $(LIBHOME)/$(LIBNAME).dll
+LIBFILE = $(LIBHOME)/$(LIBNAME).lib
+LNKFILE = $(LIBNAME).lnk
+
+INCPATH = -I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h"
+INCPATH+= -I"$(LIBHOME)/h"
+INCPATH+= -Iinclude
+
+LIBS = mmpm2.lib libuls.lib libconv.lib
+
+CFLAGS = -bt=os2 -d0 -q -bm -5s -fp5 -fpi87 -sg -oteanbmier -ei
+# max warnings:
+CFLAGS+= -wx
+# building dll:
+CFLAGS+= -bd
+# the include paths :
+CFLAGS+= $(INCPATH)
+# building SDL itself:
+CFLAGS+= -DBUILD_SDL
+
+SRCS = SDL.c SDL_assert.c SDL_error.c SDL_log.c SDL_dataqueue.c SDL_hints.c
+SRCS+= SDL_getenv.c SDL_iconv.c SDL_malloc.c SDL_qsort.c SDL_stdlib.c SDL_string.c
+SRCS+= SDL_cpuinfo.c SDL_atomic.c SDL_spinlock.c SDL_thread.c SDL_timer.c
+SRCS+= SDL_rwops.c SDL_power.c
+SRCS+= SDL_audio.c SDL_audiocvt.c SDL_audiodev.c SDL_audiotypecvt.c SDL_mixer.c SDL_wave.c
+SRCS+= SDL_events.c SDL_quit.c SDL_keyboard.c SDL_mouse.c SDL_windowevents.c &
+       SDL_clipboardevents.c SDL_dropevents.c SDL_displayevents.c SDL_gesture.c &
+       SDL_sensor.c SDL_touch.c
+SRCS+= SDL_haptic.c SDL_gamecontroller.c SDL_joystick.c
+SRCS+= SDL_render.c yuv_rgb.c SDL_yuv.c SDL_yuv_sw.c SDL_blendfillrect.c &
+       SDL_blendline.c SDL_blendpoint.c SDL_drawline.c SDL_drawpoint.c &
+       SDL_render_sw.c SDL_rotate.c
+SRCS+= SDL_blit.c SDL_blit_0.c SDL_blit_1.c SDL_blit_A.c SDL_blit_auto.c &
+       SDL_blit_copy.c SDL_blit_N.c SDL_blit_slow.c SDL_fillrect.c SDL_bmp.c &
+       SDL_pixels.c SDL_rect.c SDL_RLEaccel.c SDL_shape.c SDL_stretch.c &
+       SDL_surface.c SDL_video.c SDL_clipboard.c SDL_vulkan_utils.c SDL_egl.c
+
+SRCS+= SDL_syscond.c SDL_sysmutex.c SDL_syssem.c SDL_systhread.c SDL_systls.c
+SRCS+= SDL_systimer.c
+SRCS+= SDL_sysloadso.c
+SRCS+= SDL_sysfilesystem.c
+SRCS+= SDL_syshaptic.c SDL_sysjoystick.c
+SRCS+= SDL_dummyaudio.c SDL_diskaudio.c
+SRCS+= SDL_nullvideo.c SDL_nullframebuffer.c SDL_nullevents.c
+SRCS+= SDL_dummysensor.c
+
+SRCS+= SDL_dynapi.c
+
+OBJS = $(SRCS:.c=.obj)
+
+.extensions:
+.extensions: .lib .dll .obj .c .asm
+
+.c: ./src;./src/dynapi;./src/audio;./src/cpuinfo;./src/events;./src/file;./src/haptic;./src/joystick;./src/power;./src/render;./src/render/software;./src/sensor;./src/stdlib;./src/thread;./src/timer;./src/video;./src/video/yuv2rgb;./src/atomic;./src/audio/disk;
+.c: ./src/haptic/dummy;./src/joystick/dummy;./src/audio/dummy;./src/video/dummy;./src/sensor/dummy;
+.c: ./src/loadso/dummy;./src/filesystem/dummy;./src/timer/dummy;./src/thread/generic;
+
+all: $(DLLFILE) $(LIBFILE) .symbolic
+
+$(DLLFILE): $(OBJS) $(LNKFILE)
+    @echo * Linking: $@
+    wlink @$(LNKFILE)
+
+$(LIBFILE): $(DLLFILE)
+    @echo * Creating LIB file: $@
+    wlib -q -b -n -c -pa -s -t -zld -ii -io $* $(DLLFILE)
+
+.c.obj:
+    wcc386 $(CFLAGS) -fo=$^@ $<
+
+SDL_cpuinfo.obj: SDL_cpuinfo.c
+    wcc386 $(CFLAGS) -wcd=200 -fo=$^@ $<
+
+SDL_rwops.obj: SDL_rwops.c
+    wcc386 $(CFLAGS) -wcd=136 -fo=$^@ $<
+
+SDL_blendfillrect.obj: SDL_blendfillrect.c
+    wcc386 $(CFLAGS) -wcd=200 -fo=$^@ $<
+
+SDL_blendline.obj: SDL_blendline.c
+    wcc386 $(CFLAGS) -wcd=200 -fo=$^@ $<
+
+SDL_blendpoint.obj: SDL_blendpoint.c
+    wcc386 $(CFLAGS) -wcd=200 -fo=$^@ $<
+
+SDL_RLEaccel.obj: SDL_RLEaccel.c
+    wcc386 $(CFLAGS) -wcd=201 -fo=$^@ $<
+
+$(LNKFILE):
+    @echo * Creating linker file: $@
+    @%create $@
+    @%append $@ SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE
+    @%append $@ NAME $(DLLFILE)
+    @for %i in ($(OBJS)) do @%append $@ FILE %i
+    @%append $@ LIBPATH $(%LIB);$(LIBPATH)
+    @for %i in ($(LIBS)) do @%append $@ LIB %i
+    @%append $@ OPTION QUIET
+    @%append $@ OPTION IMPF=$(LIBHOME)/$^&.exp
+    @%append $@ OPTION MAP=$(LIBHOME)/$^&.map
+    @%append $@ OPTION DESCRIPTION '@$#libsdl org:$(VERSION)$#@$(DESCRIPTION)'
+    @%append $@ OPTION QUIET
+    @%append $@ OPTION ELIMINATE
+    @%append $@ OPTION MANYAUTODATA
+    @%append $@ OPTION OSNAME='OS/2 and eComStation'
+    @%append $@ OPTION SHOWDEAD
+
+clean: .SYMBOLIC
+    @ echo * Clean: $(LIBNAME)
+    @if exist *.obj rm *.obj
+    @if exist *.err rm *.err
+    @if exist $(LNKFILE) rm $(LNKFILE)
+
+distclean: .SYMBOLIC clean
+    @if exist $(LIBHOME)/*.exp rm $(LIBHOME)/*.exp
+    @if exist $(LIBHOME)/*.map rm $(LIBHOME)/*.map
+    @if exist $(LIBFILE) rm $(LIBFILE)
+    @if exist $(DLLFILE) rm $(DLLFILE)
diff --git a/source/Makefile.pandora b/source/Makefile.pandora
index 56f171b..f4cb668 100644
--- a/source/Makefile.pandora
+++ b/source/Makefile.pandora
@@ -12,15 +12,38 @@
 
 TARGET  = libSDL.a
 
-SOURCES = ./src/*.c ./src/audio/*.c ./src/cpuinfo/*.c ./src/events/*.c \
-	./src/file/*.c ./src/stdlib/*.c ./src/thread/*.c ./src/timer/*.c ./src/video/*.c \
-	./src/joystick/*.c ./src/haptic/*.c ./src/power/*.c ./src/video/dummy/*.c ./src/audio/disk/*.c \
-	./src/audio/dummy/*.c ./src/loadso/dlopen/*.c ./src/audio/dsp/*.c \
-	./src/thread/pthread/SDL_systhread.c ./src/thread/pthread/SDL_syssem.c \
-	./src/thread/pthread/SDL_sysmutex.c ./src/thread/pthread/SDL_syscond.c \
-	./src/joystick/linux/*.c ./src/haptic/linux/*.c ./src/timer/unix/*.c \
-	./src/atomic/*.c ./src/filesystem/unix/*.c \
-	./src/video/pandora/SDL_pandora.o ./src/video/pandora/SDL_pandora_events.o ./src/video/x11/*.c 
+SOURCES = 
+	./src/*.c \
+	./src/atomic/*.c \
+	./src/audio/*.c \
+	./src/audio/disk/*.c \
+	./src/audio/dsp/*.c \
+	./src/audio/dummy/*.c \
+	./src/cpuinfo/*.c \
+	./src/events/*.c \
+	./src/file/*.c \
+	./src/filesystem/unix/*.c \
+	./src/haptic/*.c \
+	./src/haptic/linux/*.c \
+	./src/joystick/*.c \
+	./src/joystick/linux/*.c \
+	./src/loadso/dlopen/*.c \
+	./src/power/*.c \
+	./src/sensor/*.c \
+	./src/sensor/dummy/*.c \
+	./src/stdlib/*.c \
+	./src/thread/*.c \
+	./src/thread/pthread/SDL_syscond.c \
+	./src/thread/pthread/SDL_sysmutex.c \
+	./src/thread/pthread/SDL_syssem.c \
+	./src/thread/pthread/SDL_systhread.c \
+	./src/timer/*.c \
+	./src/timer/unix/*.c \
+	./src/video/*.c \
+	./src/video/dummy/*.c \
+	./src/video/pandora/SDL_pandora.o \
+	./src/video/pandora/SDL_pandora_events.o \
+	./src/video/x11/*.c \
 	
 
 OBJECTS = $(shell echo $(SOURCES) | sed -e 's,\.c,\.o,g')
diff --git a/source/Makefile.psp b/source/Makefile.psp
index 93fb9e4..de0e50e 100644
--- a/source/Makefile.psp
+++ b/source/Makefile.psp
@@ -42,6 +42,8 @@
       src/render/software/SDL_drawpoint.o \
       src/render/software/SDL_render_sw.o \
       src/render/software/SDL_rotate.o \
+      src/sensor/SDL_sensor.o \
+      src/sensor/dummy/SDL_dummysensor.o \
       src/stdlib/SDL_getenv.o \
       src/stdlib/SDL_iconv.o \
       src/stdlib/SDL_malloc.o \
diff --git a/source/Makefile.wiz b/source/Makefile.wiz
index 0981be8..8ed58ee 100644
--- a/source/Makefile.wiz
+++ b/source/Makefile.wiz
@@ -12,14 +12,33 @@
 TARGET_STATIC  = libSDL2.a
 TARGET_SHARED  = libSDL2.so
 
-SOURCES = ./src/*.c ./src/audio/*.c ./src/cpuinfo/*.c ./src/events/*.c \
-	./src/file/*.c ./src/stdlib/*.c ./src/thread/*.c ./src/timer/*.c ./src/video/*.c \
-	./src/joystick/*.c ./src/haptic/*.c ./src/video/dummy/*.c ./src/audio/disk/*.c \
-	./src/audio/dummy/*.c ./src/loadso/dlopen/*.c ./src/audio/dsp/*.c \
-	./src/thread/pthread/SDL_systhread.c ./src/thread/pthread/SDL_syssem.c \
-	./src/thread/pthread/SDL_sysmutex.c ./src/thread/pthread/SDL_syscond.c \
-	./src/joystick/linux/*.c ./src/haptic/linux/*.c ./src/timer/unix/*.c \
-	./src/video/pandora/SDL_pandora.o ./src/video/pandora/SDL_pandora_events.o
+SOURCES = \
+	./src/*.c \
+	./src/audio/*.c \
+	./src/audio/disk/*.c \
+	./src/audio/dsp/*.c \
+	./src/audio/dummy/*.c \
+	./src/cpuinfo/*.c \
+	./src/events/*.c \
+	./src/file/*.c \
+	./src/haptic/*.c \
+	./src/haptic/linux/*.c \
+	./src/joystick/*.c \
+	./src/joystick/linux/*.c \
+	./src/loadso/dlopen/*.c \
+	./src/sensor/*.c \
+	./src/sensor/dummy/*.c \
+	./src/stdlib/*.c \
+	./src/thread/*.c \
+	./src/thread/pthread/SDL_syscond.c \
+	./src/thread/pthread/SDL_sysmutex.c \
+	./src/thread/pthread/SDL_syssem.c \
+	./src/thread/pthread/SDL_systhread.c \
+	./src/timer/*.c \
+	./src/timer/unix/*.c \
+	./src/video/*.c \
+	./src/video/dummy/*.c \
+	./src/video/pandora/*.c \
 	
 
 OBJECTS = $(shell echo $(SOURCES) | sed -e 's,\.c,\.o,g')
diff --git a/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj b/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj
index 5e18787..ede896f 100644
--- a/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj
+++ b/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj
@@ -67,6 +67,7 @@
     <ClInclude Include="..\..\include\SDL_revision.h" />
     <ClInclude Include="..\..\include\SDL_rwops.h" />
     <ClInclude Include="..\..\include\SDL_scancode.h" />
+    <ClInclude Include="..\..\include\SDL_sensor.h" />
     <ClInclude Include="..\..\include\SDL_shape.h" />
     <ClInclude Include="..\..\include\SDL_stdinc.h" />
     <ClInclude Include="..\..\include\SDL_surface.h" />
@@ -97,6 +98,7 @@
     <ClInclude Include="..\..\src\events\blank_cursor.h" />
     <ClInclude Include="..\..\src\events\default_cursor.h" />
     <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
@@ -136,6 +138,9 @@
     <ClInclude Include="..\..\src\SDL_fatal.h" />
     <ClInclude Include="..\..\src\SDL_hints_c.h" />
     <ClInclude Include="..\..\src\SDL_internal.h" />
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h" />
     <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\stdcpp\SDL_sysmutex_c.h" />
@@ -212,6 +217,7 @@
     <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
     <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
     <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
     <ClCompile Include="..\..\src\events\SDL_gesture.c" />
@@ -271,6 +277,8 @@
     <ClCompile Include="..\..\src\SDL_error.c" />
     <ClCompile Include="..\..\src\SDL_hints.c" />
     <ClCompile Include="..\..\src\SDL_log.c" />
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c" />
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
diff --git a/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters b/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters
index 9020a74..7a26d85 100644
--- a/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters
+++ b/source/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters
@@ -414,6 +414,21 @@
     <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\include\SDL_sensor.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\atomic\SDL_atomic.c">
@@ -746,5 +761,14 @@
     <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj b/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj
index 04ed367..a7df73b 100644
--- a/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj
+++ b/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj
@@ -59,6 +59,7 @@
     <ClInclude Include="..\..\include\SDL_revision.h" />
     <ClInclude Include="..\..\include\SDL_rwops.h" />
     <ClInclude Include="..\..\include\SDL_scancode.h" />
+    <ClInclude Include="..\..\include\SDL_sensor.h" />
     <ClInclude Include="..\..\include\SDL_shape.h" />
     <ClInclude Include="..\..\include\SDL_stdinc.h" />
     <ClInclude Include="..\..\include\SDL_surface.h" />
@@ -87,6 +88,7 @@
     <ClInclude Include="..\..\src\events\blank_cursor.h" />
     <ClInclude Include="..\..\src\events\default_cursor.h" />
     <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
@@ -121,6 +123,9 @@
     <ClInclude Include="..\..\src\SDL_fatal.h" />
     <ClInclude Include="..\..\src\SDL_hints_c.h" />
     <ClInclude Include="..\..\src\SDL_internal.h" />
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h" />
     <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
@@ -187,6 +192,7 @@
     <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
     <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
     <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
     <ClCompile Include="..\..\src\events\SDL_gesture.c" />
@@ -237,6 +243,8 @@
     <ClCompile Include="..\..\src\SDL_error.c" />
     <ClCompile Include="..\..\src\SDL_hints.c" />
     <ClCompile Include="..\..\src\SDL_log.c" />
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c" />
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
diff --git a/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters b/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters
index cd1af24..dba8edd 100644
--- a/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters
+++ b/source/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters
@@ -387,7 +387,24 @@
     <ClInclude Include="..\..\src\video\yuv2rgb\yuv_rgb.h">
       <Filter>Source Files</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
+    <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_sensor.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\atomic\SDL_atomic.c">
@@ -702,6 +719,17 @@
     <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb.c">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
+    <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj b/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj
index 3c76186..84b06d5 100644
--- a/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj
+++ b/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj
@@ -67,6 +67,7 @@
     <ClInclude Include="..\..\include\SDL_revision.h" />
     <ClInclude Include="..\..\include\SDL_rwops.h" />
     <ClInclude Include="..\..\include\SDL_scancode.h" />
+    <ClInclude Include="..\..\include\SDL_sensor.h" />
     <ClInclude Include="..\..\include\SDL_shape.h" />
     <ClInclude Include="..\..\include\SDL_stdinc.h" />
     <ClInclude Include="..\..\include\SDL_surface.h" />
@@ -97,6 +98,7 @@
     <ClInclude Include="..\..\src\events\blank_cursor.h" />
     <ClInclude Include="..\..\src\events\default_cursor.h" />
     <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
@@ -136,6 +138,9 @@
     <ClInclude Include="..\..\src\SDL_fatal.h" />
     <ClInclude Include="..\..\src\SDL_hints_c.h" />
     <ClInclude Include="..\..\src\SDL_internal.h" />
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
     <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
@@ -210,6 +215,7 @@
     <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
     <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
     <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
     <ClCompile Include="..\..\src\events\SDL_gesture.c" />
@@ -269,6 +275,8 @@
     <ClCompile Include="..\..\src\SDL_error.c" />
     <ClCompile Include="..\..\src\SDL_hints.c" />
     <ClCompile Include="..\..\src\SDL_log.c" />
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c" />
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
diff --git a/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters b/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters
index dafd707..6a52b1c 100644
--- a/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters
+++ b/source/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters
@@ -408,6 +408,21 @@
     <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\include\SDL_sensor.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\atomic\SDL_atomic.c">
@@ -743,5 +758,14 @@
     <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/source/VisualC/SDL/SDL.vcxproj b/source/VisualC/SDL/SDL.vcxproj
index 847a2a4..505adcd 100644
--- a/source/VisualC/SDL/SDL.vcxproj
+++ b/source/VisualC/SDL/SDL.vcxproj
@@ -80,6 +80,18 @@
     <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
     <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath)</LibraryPath>
   </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
+  </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <PreBuildEvent>
       <Command>
@@ -109,7 +121,7 @@
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
@@ -140,7 +152,7 @@
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
@@ -174,7 +186,7 @@
       <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
@@ -206,7 +218,7 @@
       <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ResourceCompile>
     <Link>
-      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
@@ -264,6 +276,7 @@
     <ClInclude Include="..\..\include\SDL_revision.h" />
     <ClInclude Include="..\..\include\SDL_rwops.h" />
     <ClInclude Include="..\..\include\SDL_scancode.h" />
+    <ClInclude Include="..\..\include\SDL_sensor.h" />
     <ClInclude Include="..\..\include\SDL_shape.h" />
     <ClInclude Include="..\..\include\SDL_stdinc.h" />
     <ClInclude Include="..\..\include\SDL_surface.h" />
@@ -306,6 +319,7 @@
     <ClInclude Include="..\..\src\events\blank_cursor.h" />
     <ClInclude Include="..\..\src\events\default_cursor.h" />
     <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
     <ClInclude Include="..\..\src\events\SDL_events_c.h" />
     <ClInclude Include="..\..\src\events\SDL_gesture_c.h" />
@@ -318,6 +332,8 @@
     <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
     <ClInclude Include="..\..\src\haptic\windows\SDL_xinputhaptic_c.h" />
+    <ClInclude Include="..\..\src\joystick\hidapi\controller_type.h" />
+    <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
     <ClInclude Include="..\..\src\joystick\SDL_sysjoystick.h" />
     <ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h" />
@@ -343,6 +359,9 @@
     <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
     <ClInclude Include="..\..\src\SDL_dataqueue.h" />
     <ClInclude Include="..\..\src\SDL_error_c.h" />
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h" />
     <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
@@ -397,6 +416,7 @@
     <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
     <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
     <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
     <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
     <ClCompile Include="..\..\src\events\SDL_events.c" />
     <ClCompile Include="..\..\src\events\SDL_gesture.c" />
@@ -411,6 +431,12 @@
     <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
     <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
     <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
+    <ClCompile Include="..\..\src\hidapi\windows\hid.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_ps4.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
     <ClCompile Include="..\..\src\joystick\SDL_gamecontroller.c" />
     <ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
     <ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c" />
@@ -418,6 +444,7 @@
     <ClCompile Include="..\..\src\joystick\windows\SDL_windowsjoystick.c" />
     <ClCompile Include="..\..\src\joystick\windows\SDL_xinputjoystick.c" />
     <ClCompile Include="..\..\src\libm\e_atan2.c" />
+    <ClCompile Include="..\..\src\libm\e_exp.c" />
     <ClCompile Include="..\..\src\libm\e_fmod.c" />
     <ClCompile Include="..\..\src\libm\e_log.c" />
     <ClCompile Include="..\..\src\libm\e_log10.c" />
@@ -463,6 +490,8 @@
     <ClCompile Include="..\..\src\SDL_error.c" />
     <ClCompile Include="..\..\src\SDL_hints.c" />
     <ClCompile Include="..\..\src\SDL_log.c" />
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c" />
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
     <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
@@ -522,4 +551,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/source/VisualC/SDL/SDL.vcxproj.filters b/source/VisualC/SDL/SDL.vcxproj.filters
index 8d55a58..75a9e4f 100644
--- a/source/VisualC/SDL/SDL.vcxproj.filters
+++ b/source/VisualC/SDL/SDL.vcxproj.filters
@@ -1,460 +1,479 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <Filter Include="API Headers">
-      <UniqueIdentifier>{395b3af0-33d0-411b-b153-de1676bf1ef8}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="..\..\include\begin_code.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\close_code.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_assert.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_atomic.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_audio.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_bits.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_blendmode.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_clipboard.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_config.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_config_windows.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_copying.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_cpuinfo.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_egl.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_endian.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_error.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_events.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_filesystem.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_gamecontroller.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_gesture.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_haptic.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_hints.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_joystick.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_keyboard.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_keycode.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_loadso.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_log.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_main.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_messagebox.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_mouse.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_mutex.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_name.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengl.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengl_glext.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengles.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengles2.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengles2_gl2.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengles2_gl2ext.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengles2_gl2platform.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_opengles2_khrplatform.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_pixels.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_platform.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_power.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_quit.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_rect.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_render.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_revision.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_rwops.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_scancode.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_shape.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_stdinc.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_surface.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_system.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_syswm.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_assert.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_common.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_compare.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_crc32.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_font.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_fuzzer.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_harness.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_images.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_log.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_md5.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_test_random.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_thread.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_timer.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_touch.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_types.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_version.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_video.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\include\SDL_vulkan.h">
-      <Filter>API Headers</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\src\audio\directsound\SDL_directsound.h" />
-    <ClInclude Include="..\..\src\audio\disk\SDL_diskaudio.h" />
-    <ClInclude Include="..\..\src\audio\dummy\SDL_dummyaudio.h" />
-    <ClInclude Include="..\..\src\audio\SDL_audio_c.h" />
-    <ClInclude Include="..\..\src\audio\SDL_audiodev_c.h" />
-    <ClInclude Include="..\..\src\audio\SDL_sysaudio.h" />
-    <ClInclude Include="..\..\src\audio\SDL_wave.h" />
-    <ClInclude Include="..\..\src\audio\wasapi\SDL_wasapi.h" />
-    <ClInclude Include="..\..\src\audio\winmm\SDL_winmm.h" />
-    <ClInclude Include="..\..\src\core\windows\SDL_directx.h" />
-    <ClInclude Include="..\..\src\core\windows\SDL_windows.h" />
-    <ClInclude Include="..\..\src\core\windows\SDL_xinput.h" />
-    <ClInclude Include="..\..\src\dynapi\SDL_dynapi.h" />
-    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_overrides.h" />
-    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_procs.h" />
-    <ClInclude Include="..\..\src\events\blank_cursor.h" />
-    <ClInclude Include="..\..\src\events\default_cursor.h" />
-    <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_events_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_gesture_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_sysevents.h" />
-    <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
-    <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
-    <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
-    <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
-    <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
-    <ClInclude Include="..\..\src\haptic\windows\SDL_xinputhaptic_c.h" />
-    <ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
-    <ClInclude Include="..\..\src\joystick\SDL_sysjoystick.h" />
-    <ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h" />
-    <ClInclude Include="..\..\src\joystick\windows\SDL_windowsjoystick_c.h" />
-    <ClInclude Include="..\..\src\joystick\windows\SDL_xinputjoystick_c.h" />
-    <ClInclude Include="..\..\src\libm\math_libm.h" />
-    <ClInclude Include="..\..\src\libm\math_private.h" />
-    <ClInclude Include="..\..\src\render\opengl\SDL_glfuncs.h" />
-    <ClInclude Include="..\..\src\render\opengl\SDL_shaders_gl.h" />
-    <ClInclude Include="..\..\src\render\opengles\SDL_glesfuncs.h" />
-    <ClInclude Include="..\..\src\render\SDL_d3dmath.h" />
-    <ClInclude Include="..\..\src\render\SDL_sysrender.h" />
-    <ClInclude Include="..\..\src\render\SDL_yuv_sw_c.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_blendfillrect.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_blendline.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_blendpoint.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_draw.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
-    <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
-    <ClInclude Include="..\..\src\SDL_dataqueue.h" />
-    <ClInclude Include="..\..\src\SDL_error_c.h" />
-    <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
-    <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
-    <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
-    <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
-    <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />
-    <ClInclude Include="..\..\src\video\dummy\SDL_nullframebuffer_c.h" />
-    <ClInclude Include="..\..\src\video\dummy\SDL_nullvideo.h" />
-    <ClInclude Include="..\..\src\video\SDL_blit.h" />
-    <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
-    <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
-    <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
-    <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
-    <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
-    <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
-    <ClInclude Include="..\..\src\video\SDL_shape_internals.h" />
-    <ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
-    <ClInclude Include="..\..\src\video\SDL_vulkan_internal.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_vkeys.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsclipboard.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsevents.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsframebuffer.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowskeyboard.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsmessagebox.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsmodes.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />
-    <ClInclude Include="..\..\src\video\windows\SDL_windowswindow.h" />
-    <ClInclude Include="..\..\src\video\windows\wmmsg.h" />
-    <ClInclude Include="..\..\src\video\SDL_yuv_c.h" />
-    <ClInclude Include="..\..\src\video\yuv2rgb\yuv_rgb.h" />
-    <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
-    <ClInclude Include="..\..\src\render\direct3d\SDL_shaders_d3d.h" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="..\..\src\libm\e_atan2.c" />
-    <ClCompile Include="..\..\src\libm\e_log.c" />
-    <ClCompile Include="..\..\src\libm\e_log10.c" />
-    <ClCompile Include="..\..\src\libm\e_pow.c" />
-    <ClCompile Include="..\..\src\libm\e_rem_pio2.c" />
-    <ClCompile Include="..\..\src\libm\e_sqrt.c" />
-    <ClCompile Include="..\..\src\libm\k_cos.c" />
-    <ClCompile Include="..\..\src\libm\k_rem_pio2.c" />
-    <ClCompile Include="..\..\src\libm\k_sin.c" />
-    <ClCompile Include="..\..\src\libm\k_tan.c" />
-    <ClCompile Include="..\..\src\libm\s_atan.c" />
-    <ClCompile Include="..\..\src\libm\s_copysign.c" />
-    <ClCompile Include="..\..\src\libm\s_cos.c" />
-    <ClCompile Include="..\..\src\libm\s_fabs.c" />
-    <ClCompile Include="..\..\src\libm\s_floor.c" />
-    <ClCompile Include="..\..\src\libm\s_scalbn.c" />
-    <ClCompile Include="..\..\src\libm\s_sin.c" />
-    <ClCompile Include="..\..\src\libm\s_tan.c" />
-    <ClCompile Include="..\..\src\SDL.c" />
-    <ClCompile Include="..\..\src\SDL_assert.c" />
-    <ClCompile Include="..\..\src\atomic\SDL_atomic.c" />
-    <ClCompile Include="..\..\src\audio\SDL_audio.c" />
-    <ClCompile Include="..\..\src\audio\SDL_audiocvt.c" />
-    <ClCompile Include="..\..\src\audio\SDL_audiodev.c" />
-    <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_blendfillrect.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_blendline.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_blendpoint.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_0.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_1.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_A.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_auto.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_copy.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_N.c" />
-    <ClCompile Include="..\..\src\video\SDL_blit_slow.c" />
-    <ClCompile Include="..\..\src\video\SDL_bmp.c" />
-    <ClCompile Include="..\..\src\video\SDL_clipboard.c" />
-    <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
-    <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
-    <ClCompile Include="..\..\src\render\SDL_d3dmath.c" />
-    <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
-    <ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c" />
-    <ClCompile Include="..\..\src\audio\directsound\SDL_directsound.c" />
-    <ClCompile Include="..\..\src\audio\disk\SDL_diskaudio.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
-    <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
-    <ClCompile Include="..\..\src\audio\dummy\SDL_dummyaudio.c" />
-    <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
-    <ClCompile Include="..\..\src\video\SDL_egl.c" />
-    <ClCompile Include="..\..\src\SDL_dataqueue.c" />
-    <ClCompile Include="..\..\src\SDL_error.c" />
-    <ClCompile Include="..\..\src\events\SDL_events.c" />
-    <ClCompile Include="..\..\src\video\SDL_fillrect.c" />
-    <ClCompile Include="..\..\src\joystick\SDL_gamecontroller.c" />
-    <ClCompile Include="..\..\src\events\SDL_gesture.c" />
-    <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
-    <ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
-    <ClCompile Include="..\..\src\SDL_hints.c" />
-    <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
-    <ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
-    <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
-    <ClCompile Include="..\..\src\SDL_log.c" />
-    <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
-    <ClCompile Include="..\..\src\audio\SDL_mixer.c" />
-    <ClCompile Include="..\..\src\joystick\windows\SDL_mmjoystick.c" />
-    <ClCompile Include="..\..\src\events\SDL_mouse.c" />
-    <ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" />
-    <ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" />
-    <ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" />
-    <ClCompile Include="..\..\src\video\SDL_pixels.c" />
-    <ClCompile Include="..\..\src\power\SDL_power.c" />
-    <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
-    <ClCompile Include="..\..\src\events\SDL_quit.c" />
-    <ClCompile Include="..\..\src\video\SDL_rect.c" />
-    <ClCompile Include="..\..\src\render\SDL_render.c" />
-    <ClCompile Include="..\..\src\render\direct3d\SDL_render_d3d.c" />
-    <ClCompile Include="..\..\src\render\direct3d11\SDL_render_d3d11.c" />
-    <ClCompile Include="..\..\src\render\opengl\SDL_render_gl.c" />
-    <ClCompile Include="..\..\src\render\opengles2\SDL_render_gles2.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
-    <ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
-    <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
-    <ClCompile Include="..\..\src\file\SDL_rwops.c" />
-    <ClCompile Include="..\..\src\render\opengl\SDL_shaders_gl.c" />
-    <ClCompile Include="..\..\src\render\opengles2\SDL_shaders_gles2.c" />
-    <ClCompile Include="..\..\src\video\SDL_shape.c" />
-    <ClCompile Include="..\..\src\atomic\SDL_spinlock.c" />
-    <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
-    <ClCompile Include="..\..\src\video\SDL_stretch.c" />
-    <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
-    <ClCompile Include="..\..\src\video\SDL_surface.c" />
-    <ClCompile Include="..\..\src\thread\generic\SDL_syscond.c" />
-    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
-    <ClCompile Include="..\..\src\loadso\windows\SDL_sysloadso.c" />
-    <ClCompile Include="..\..\src\thread\windows\SDL_sysmutex.c" />
-    <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
-    <ClCompile Include="..\..\src\thread\windows\SDL_syssem.c" />
-    <ClCompile Include="..\..\src\thread\windows\SDL_systhread.c" />
-    <ClCompile Include="..\..\src\timer\windows\SDL_systimer.c" />
-    <ClCompile Include="..\..\src\thread\windows\SDL_systls.c" />
-    <ClCompile Include="..\..\src\thread\SDL_thread.c" />
-    <ClCompile Include="..\..\src\timer\SDL_timer.c" />
-    <ClCompile Include="..\..\src\events\SDL_touch.c" />
-    <ClCompile Include="..\..\src\video\SDL_video.c" />
-    <ClCompile Include="..\..\src\audio\SDL_wave.c" />
-    <ClCompile Include="..\..\src\events\SDL_windowevents.c" />
-    <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsclipboard.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
-    <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
-    <ClCompile Include="..\..\src\joystick\windows\SDL_windowsjoystick.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowswindow.c" />
-    <ClCompile Include="..\..\src\audio\winmm\SDL_winmm.c" />
-    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
-    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
-    <ClCompile Include="..\..\src\core\windows\SDL_xinput.c" />
-    <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
-    <ClCompile Include="..\..\src\joystick\windows\SDL_xinputjoystick.c" />
-    <ClCompile Include="..\..\src\render\SDL_yuv_sw.c" />
-    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
-    <ClCompile Include="..\..\src\video\SDL_vulkan_utils.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />
-    <ClCompile Include="..\..\src\libm\e_fmod.c" />
-    <ClCompile Include="..\..\src\video\SDL_yuv.c" />
-    <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb.c" />
-    <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
-    <ClCompile Include="..\..\src\render\direct3d\SDL_shaders_d3d.c" />
-  </ItemGroup>
-  <ItemGroup>
-    <ResourceCompile Include="..\..\src\main\windows\version.rc" />
-  </ItemGroup>
-</Project>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="API Headers">
+      <UniqueIdentifier>{395b3af0-33d0-411b-b153-de1676bf1ef8}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\include\begin_code.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\close_code.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_assert.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_atomic.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_audio.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_bits.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_blendmode.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_clipboard.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_config.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_config_windows.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_copying.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_cpuinfo.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_egl.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_endian.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_error.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_events.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_filesystem.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_gamecontroller.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_gesture.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_haptic.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_hints.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_joystick.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_keyboard.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_keycode.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_loadso.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_log.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_main.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_messagebox.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_mouse.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_mutex.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_name.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengl.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengl_glext.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengles.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengles2.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengles2_gl2.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengles2_gl2ext.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengles2_gl2platform.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_opengles2_khrplatform.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_pixels.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_platform.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_power.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_quit.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_rect.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_render.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_revision.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_rwops.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_scancode.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_sensor.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_shape.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_stdinc.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_surface.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_system.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_syswm.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_assert.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_common.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_compare.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_crc32.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_font.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_fuzzer.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_harness.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_images.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_log.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_md5.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_test_random.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_thread.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_timer.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_touch.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_types.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_version.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_video.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\SDL_vulkan.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\audio\directsound\SDL_directsound.h" />
+    <ClInclude Include="..\..\src\audio\disk\SDL_diskaudio.h" />
+    <ClInclude Include="..\..\src\audio\dummy\SDL_dummyaudio.h" />
+    <ClInclude Include="..\..\src\audio\SDL_audio_c.h" />
+    <ClInclude Include="..\..\src\audio\SDL_audiodev_c.h" />
+    <ClInclude Include="..\..\src\audio\SDL_sysaudio.h" />
+    <ClInclude Include="..\..\src\audio\SDL_wave.h" />
+    <ClInclude Include="..\..\src\audio\wasapi\SDL_wasapi.h" />
+    <ClInclude Include="..\..\src\audio\winmm\SDL_winmm.h" />
+    <ClInclude Include="..\..\src\core\windows\SDL_directx.h" />
+    <ClInclude Include="..\..\src\core\windows\SDL_windows.h" />
+    <ClInclude Include="..\..\src\core\windows\SDL_xinput.h" />
+    <ClInclude Include="..\..\src\dynapi\SDL_dynapi.h" />
+    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_overrides.h" />
+    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_procs.h" />
+    <ClInclude Include="..\..\src\events\blank_cursor.h" />
+    <ClInclude Include="..\..\src\events\default_cursor.h" />
+    <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_displayevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_events_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_gesture_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_sysevents.h" />
+    <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
+    <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
+    <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
+    <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
+    <ClInclude Include="..\..\src\haptic\windows\SDL_xinputhaptic_c.h" />
+    <ClInclude Include="..\..\src\joystick\hidapi\controller_type.h" />
+    <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
+    <ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
+    <ClInclude Include="..\..\src\joystick\SDL_sysjoystick.h" />
+    <ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h" />
+    <ClInclude Include="..\..\src\joystick\windows\SDL_windowsjoystick_c.h" />
+    <ClInclude Include="..\..\src\joystick\windows\SDL_xinputjoystick_c.h" />
+    <ClInclude Include="..\..\src\libm\math_libm.h" />
+    <ClInclude Include="..\..\src\libm\math_private.h" />
+    <ClInclude Include="..\..\src\render\direct3d\SDL_shaders_d3d.h" />
+    <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
+    <ClInclude Include="..\..\src\render\opengl\SDL_glfuncs.h" />
+    <ClInclude Include="..\..\src\render\opengl\SDL_shaders_gl.h" />
+    <ClInclude Include="..\..\src\render\opengles\SDL_glesfuncs.h" />
+    <ClInclude Include="..\..\src\render\SDL_d3dmath.h" />
+    <ClInclude Include="..\..\src\render\SDL_sysrender.h" />
+    <ClInclude Include="..\..\src\render\SDL_yuv_sw_c.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_blendfillrect.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_blendline.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_blendpoint.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_draw.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
+    <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
+    <ClInclude Include="..\..\src\SDL_dataqueue.h" />
+    <ClInclude Include="..\..\src\SDL_error_c.h" />
+    <ClInclude Include="..\..\src\sensor\dummy\SDL_dummysensor.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_sensor_c.h" />
+    <ClInclude Include="..\..\src\sensor\SDL_syssensor.h" />
+    <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
+    <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
+    <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
+    <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
+    <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />
+    <ClInclude Include="..\..\src\video\dummy\SDL_nullframebuffer_c.h" />
+    <ClInclude Include="..\..\src\video\dummy\SDL_nullvideo.h" />
+    <ClInclude Include="..\..\src\video\SDL_blit.h" />
+    <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
+    <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
+    <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
+    <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
+    <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
+    <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
+    <ClInclude Include="..\..\src\video\SDL_shape_internals.h" />
+    <ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
+    <ClInclude Include="..\..\src\video\SDL_vulkan_internal.h" />
+    <ClInclude Include="..\..\src\video\SDL_yuv_c.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_vkeys.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsclipboard.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsevents.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsframebuffer.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowskeyboard.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsmessagebox.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsmodes.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />
+    <ClInclude Include="..\..\src\video\windows\SDL_windowswindow.h" />
+    <ClInclude Include="..\..\src\video\windows\wmmsg.h" />
+    <ClInclude Include="..\..\src\video\yuv2rgb\yuv_rgb.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\atomic\SDL_atomic.c" />
+    <ClCompile Include="..\..\src\atomic\SDL_spinlock.c" />
+    <ClCompile Include="..\..\src\audio\directsound\SDL_directsound.c" />
+    <ClCompile Include="..\..\src\audio\disk\SDL_diskaudio.c" />
+    <ClCompile Include="..\..\src\audio\dummy\SDL_dummyaudio.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audio.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audiocvt.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audiodev.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c" />
+    <ClCompile Include="..\..\src\audio\SDL_mixer.c" />
+    <ClCompile Include="..\..\src\audio\SDL_wave.c" />
+    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
+    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
+    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
+    <ClCompile Include="..\..\src\audio\winmm\SDL_winmm.c" />
+    <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
+    <ClCompile Include="..\..\src\core\windows\SDL_xinput.c" />
+    <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
+    <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
+    <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_displayevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
+    <ClCompile Include="..\..\src\events\SDL_events.c" />
+    <ClCompile Include="..\..\src\events\SDL_gesture.c" />
+    <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
+    <ClCompile Include="..\..\src\events\SDL_mouse.c" />
+    <ClCompile Include="..\..\src\events\SDL_quit.c" />
+    <ClCompile Include="..\..\src\events\SDL_touch.c" />
+    <ClCompile Include="..\..\src\events\SDL_windowevents.c" />
+    <ClCompile Include="..\..\src\file\SDL_rwops.c" />
+    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
+    <ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
+    <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
+    <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
+    <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
+    <ClCompile Include="..\..\src\hidapi\windows\hid.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_ps4.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
+    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
+    <ClCompile Include="..\..\src\joystick\SDL_gamecontroller.c" />
+    <ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
+    <ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c" />
+    <ClCompile Include="..\..\src\joystick\windows\SDL_mmjoystick.c" />
+    <ClCompile Include="..\..\src\joystick\windows\SDL_windowsjoystick.c" />
+    <ClCompile Include="..\..\src\joystick\windows\SDL_xinputjoystick.c" />
+    <ClCompile Include="..\..\src\libm\e_atan2.c" />
+    <ClCompile Include="..\..\src\libm\e_exp.c" />
+    <ClCompile Include="..\..\src\libm\e_fmod.c" />
+    <ClCompile Include="..\..\src\libm\e_log.c" />
+    <ClCompile Include="..\..\src\libm\e_log10.c" />
+    <ClCompile Include="..\..\src\libm\e_pow.c" />
+    <ClCompile Include="..\..\src\libm\e_rem_pio2.c" />
+    <ClCompile Include="..\..\src\libm\e_sqrt.c" />
+    <ClCompile Include="..\..\src\libm\k_cos.c" />
+    <ClCompile Include="..\..\src\libm\k_rem_pio2.c" />
+    <ClCompile Include="..\..\src\libm\k_sin.c" />
+    <ClCompile Include="..\..\src\libm\k_tan.c" />
+    <ClCompile Include="..\..\src\libm\s_atan.c" />
+    <ClCompile Include="..\..\src\libm\s_copysign.c" />
+    <ClCompile Include="..\..\src\libm\s_cos.c" />
+    <ClCompile Include="..\..\src\libm\s_fabs.c" />
+    <ClCompile Include="..\..\src\libm\s_floor.c" />
+    <ClCompile Include="..\..\src\libm\s_scalbn.c" />
+    <ClCompile Include="..\..\src\libm\s_sin.c" />
+    <ClCompile Include="..\..\src\libm\s_tan.c" />
+    <ClCompile Include="..\..\src\loadso\windows\SDL_sysloadso.c" />
+    <ClCompile Include="..\..\src\power\SDL_power.c" />
+    <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
+    <ClCompile Include="..\..\src\render\direct3d\SDL_render_d3d.c" />
+    <ClCompile Include="..\..\src\render\direct3d\SDL_shaders_d3d.c" />
+    <ClCompile Include="..\..\src\render\direct3d11\SDL_render_d3d11.c" />
+    <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
+    <ClCompile Include="..\..\src\render\opengl\SDL_render_gl.c" />
+    <ClCompile Include="..\..\src\render\opengl\SDL_shaders_gl.c" />
+    <ClCompile Include="..\..\src\render\opengles2\SDL_render_gles2.c" />
+    <ClCompile Include="..\..\src\render\opengles2\SDL_shaders_gles2.c" />
+    <ClCompile Include="..\..\src\render\SDL_d3dmath.c" />
+    <ClCompile Include="..\..\src\render\SDL_render.c" />
+    <ClCompile Include="..\..\src\render\SDL_yuv_sw.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_blendfillrect.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_blendline.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_blendpoint.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
+    <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
+    <ClCompile Include="..\..\src\SDL.c" />
+    <ClCompile Include="..\..\src\SDL_assert.c" />
+    <ClCompile Include="..\..\src\SDL_dataqueue.c" />
+    <ClCompile Include="..\..\src\SDL_error.c" />
+    <ClCompile Include="..\..\src\SDL_hints.c" />
+    <ClCompile Include="..\..\src\SDL_log.c" />
+    <ClCompile Include="..\..\src\sensor\dummy\SDL_dummysensor.c" />
+    <ClCompile Include="..\..\src\sensor\SDL_sensor.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
+    <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
+    <ClCompile Include="..\..\src\thread\generic\SDL_syscond.c" />
+    <ClCompile Include="..\..\src\thread\SDL_thread.c" />
+    <ClCompile Include="..\..\src\thread\windows\SDL_sysmutex.c" />
+    <ClCompile Include="..\..\src\thread\windows\SDL_syssem.c" />
+    <ClCompile Include="..\..\src\thread\windows\SDL_systhread.c" />
+    <ClCompile Include="..\..\src\thread\windows\SDL_systls.c" />
+    <ClCompile Include="..\..\src\timer\SDL_timer.c" />
+    <ClCompile Include="..\..\src\timer\windows\SDL_systimer.c" />
+    <ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" />
+    <ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" />
+    <ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_0.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_1.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_A.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_auto.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_copy.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_N.c" />
+    <ClCompile Include="..\..\src\video\SDL_blit_slow.c" />
+    <ClCompile Include="..\..\src\video\SDL_bmp.c" />
+    <ClCompile Include="..\..\src\video\SDL_clipboard.c" />
+    <ClCompile Include="..\..\src\video\SDL_egl.c" />
+    <ClCompile Include="..\..\src\video\SDL_fillrect.c" />
+    <ClCompile Include="..\..\src\video\SDL_pixels.c" />
+    <ClCompile Include="..\..\src\video\SDL_rect.c" />
+    <ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
+    <ClCompile Include="..\..\src\video\SDL_shape.c" />
+    <ClCompile Include="..\..\src\video\SDL_stretch.c" />
+    <ClCompile Include="..\..\src\video\SDL_surface.c" />
+    <ClCompile Include="..\..\src\video\SDL_video.c" />
+    <ClCompile Include="..\..\src\video\SDL_vulkan_utils.c" />
+    <ClCompile Include="..\..\src\video\SDL_yuv.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsclipboard.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowswindow.c" />
+    <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\src\main\windows\version.rc" />
+  </ItemGroup>
+</Project>
diff --git a/source/VisualC/SDLmain/SDLmain.vcxproj b/source/VisualC/SDLmain/SDLmain.vcxproj
index 239a76e..adf5b8d 100644
--- a/source/VisualC/SDLmain/SDLmain.vcxproj
+++ b/source/VisualC/SDLmain/SDLmain.vcxproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
diff --git a/source/VisualC/tests/testvulkan/testvulkan.vcproj b/source/VisualC/tests/testvulkan/testvulkan.vcproj
deleted file mode 100644
index e413b52..0000000
--- a/source/VisualC/tests/testvulkan/testvulkan.vcproj
+++ /dev/null
@@ -1,355 +0,0 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="9.00"
-	Name="testvulkan"
-	ProjectGUID="{0D604DFD-AAB6-442C-9368-F91A344146AB}"
-	RootNamespace="testvulkan"
-	TargetFrameworkVersion="131072"
-	>
-	<Platforms>
-		<Platform
-			Name="Win32"
-		/>
-		<Platform
-			Name="x64"
-		/>
-	</Platforms>
-	<ToolFiles>
-	</ToolFiles>
-	<Configurations>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
-			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)\"
-			ConfigurationType="1"
-			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC70.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-				PreprocessorDefinitions="_DEBUG"
-				MkTypLibCompatible="true"
-				SuppressStartupBanner="true"
-				TargetEnvironment="1"
-				TypeLibraryName=".\Debug/testvulkan.tlb"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="&quot;$(SolutionDir)/../include&quot;"
-				AdditionalUsingDirectories=""
-				PreprocessorDefinitions="_DEBUG,WIN32,_WINDOWS,HAVE_OPENGL"
-				RuntimeLibrary="2"
-				WarningLevel="3"
-				DebugInformationFormat="1"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				GenerateDebugInformation="true"
-				SubSystem="2"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug|x64"
-			OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
-			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)\"
-			ConfigurationType="1"
-			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC70.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-				PreprocessorDefinitions="_DEBUG"
-				MkTypLibCompatible="true"
-				SuppressStartupBanner="true"
-				TargetEnvironment="3"
-				TypeLibraryName=".\Debug/testvulkan.tlb"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="&quot;$(SolutionDir)/../include&quot;"
-				AdditionalUsingDirectories=""
-				PreprocessorDefinitions="_DEBUG,WIN32,_WINDOWS,HAVE_OPENGL"
-				RuntimeLibrary="3"
-				WarningLevel="3"
-				DebugInformationFormat="1"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				GenerateDebugInformation="true"
-				SubSystem="2"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
-			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)\"
-			ConfigurationType="1"
-			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC70.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-				PreprocessorDefinitions="NDEBUG"
-				MkTypLibCompatible="true"
-				SuppressStartupBanner="true"
-				TargetEnvironment="1"
-				TypeLibraryName=".\Release/testvulkan.tlb"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="&quot;$(SolutionDir)/../include&quot;"
-				AdditionalUsingDirectories=""
-				PreprocessorDefinitions="NDEBUG,WIN32,_WINDOWS,HAVE_OPENGL"
-				RuntimeLibrary="2"
-				WarningLevel="3"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				SubSystem="2"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release|x64"
-			OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
-			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)\"
-			ConfigurationType="1"
-			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC70.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-				PreprocessorDefinitions="NDEBUG"
-				MkTypLibCompatible="true"
-				SuppressStartupBanner="true"
-				TargetEnvironment="3"
-				TypeLibraryName=".\Release/testvulkan.tlb"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="&quot;$(SolutionDir)/../include&quot;"
-				AdditionalUsingDirectories=""
-				PreprocessorDefinitions="NDEBUG,WIN32,_WINDOWS,HAVE_OPENGL"
-				RuntimeLibrary="2"
-				WarningLevel="3"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				SubSystem="2"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-	</Configurations>
-	<References>
-		<ProjectReference
-			ReferencedProjectIdentifier="{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}"
-			CopyLocal="false"
-			CopyLocalDependencies="false"
-			CopyLocalSatelliteAssemblies="false"
-			RelativePathToProject=".\SDL\SDL_VS2008.vcproj"
-		/>
-		<ProjectReference
-			ReferencedProjectIdentifier="{DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}"
-			CopyLocal="false"
-			CopyLocalDependencies="false"
-			CopyLocalSatelliteAssemblies="false"
-			RelativePathToProject=".\SDLmain\SDLmain_VS2008.vcproj"
-		/>
-		<ProjectReference
-			ReferencedProjectIdentifier="{DA956FD3-E143-46F2-9FE5-C77BEBC56B1A}"
-			CopyLocal="false"
-			CopyLocalDependencies="false"
-			CopyLocalSatelliteAssemblies="false"
-			RelativePathToProject=".\SDLtest\SDLtest_VS2008.vcproj"
-		/>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\..\..\test\testvulkan.c"
-			>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/source/WhatsNew.txt b/source/WhatsNew.txt
index c574946..56de7b6 100644
--- a/source/WhatsNew.txt
+++ b/source/WhatsNew.txt
@@ -2,6 +2,47 @@
 This is a list of major changes in SDL's version history.
 
 ---------------------------------------------------------------------------
+2.0.9:
+---------------------------------------------------------------------------
+
+General:
+* Added a new sensor API, initialized by passing SDL_INIT_SENSOR to SDL_Init(), and defined in SDL_sensor.h
+* Added an event SDL_SENSORUPDATE which is sent when a sensor is updated
+* Added SDL_GetDisplayOrientation() to return the current display orientation
+* Added an event SDL_DISPLAYEVENT which is sent when the display orientation changes
+* Added HIDAPI joystick drivers for more consistent support for Xbox, PS4 and Nintendo Switch Pro controller support across platforms. (Thanks to Valve for contributing the PS4 and Nintendo Switch Pro controller support)
+* Added support for many other popular game controllers
+* Added SDL_JoystickGetDevicePlayerIndex(), SDL_JoystickGetPlayerIndex(), and SDL_GameControllerGetPlayerIndex() to get the player index for a controller. For XInput controllers this returns the XInput index for the controller.
+* Added SDL_GameControllerRumble() and SDL_JoystickRumble() which allow simple rumble without using the haptics API
+* Added SDL_GameControllerMappingForDeviceIndex() to get the mapping for a controller before it's opened
+* Added the hint SDL_HINT_MOUSE_DOUBLE_CLICK_TIME to control the mouse double-click time
+* Added the hint SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS to control the mouse double-click radius, in pixels
+* Added SDL_HasColorKey() to return whether a surface has a colorkey active
+* Added SDL_HasAVX512F() to return whether the CPU has AVX-512F features
+* Added SDL_IsTablet() to return whether the application is running on a tablet
+* Added SDL_THREAD_PRIORITY_TIME_CRITICAL for threads that must run at the highest priority
+
+Mac OS X:
+* Fixed black screen at start on Mac OS X Mojave
+
+Linux:
+* Added SDL_LinuxSetThreadPriority() to allow adjusting the thread priority of native threads using RealtimeKit if available.
+
+iOS:
+* Fixed Asian IME input
+
+Android:
+* Updated required Android SDK to API 26, to match Google's new App Store requirements
+* Added support for wired USB Xbox, PS4, and Nintendo Switch Pro controllers
+* Added support for relative mouse mode on Android 7.0 and newer (except where it's broken, on Chromebooks and when in DeX mode with Samsung Experience 9.0)
+* Added support for custom mouse cursors on Android 7.0 and newer
+* Added the hint SDL_HINT_ANDROID_TRAP_BACK_BUTTON to control whether the back button will back out of the app (the default) or be passed to the application as SDL_SCANCODE_AC_BACK
+* Added SDL_AndroidBackButton() to trigger the Android system back button behavior when handling the back button in the application
+* Added SDL_IsChromebook() to return whether the app is running in the Chromebook Android runtime
+* Added SDL_IsDeXMode() to return whether the app is running while docked in the Samsung DeX
+
+
+---------------------------------------------------------------------------
 2.0.8:
 ---------------------------------------------------------------------------
 
diff --git a/source/Xcode-iOS/Demos/Demos.xcodeproj/project.pbxproj b/source/Xcode-iOS/Demos/Demos.xcodeproj/project.pbxproj
old mode 100755
new mode 100644
index d4ace90..2939084
--- a/source/Xcode-iOS/Demos/Demos.xcodeproj/project.pbxproj
+++ b/source/Xcode-iOS/Demos/Demos.xcodeproj/project.pbxproj
@@ -975,6 +975,7 @@
 		C01FCF4F08A954540054247B /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				GCC_OPTIMIZATION_LEVEL = 0;
 				HEADER_SEARCH_PATHS = ../../include;
@@ -988,6 +989,7 @@
 		C01FCF5008A954540054247B /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				HEADER_SEARCH_PATHS = ../../include;
 				PRELINK_LIBS = "";
diff --git a/source/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/source/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
old mode 100755
new mode 100644
index 0032309..aace16b
--- a/source/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
+++ b/source/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
@@ -94,6 +94,7 @@
 		4D75171A1EE1D32200820EEA /* SDL_uikitmetalview.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D7517191EE1D32200820EEA /* SDL_uikitmetalview.h */; };
 		4D75171F1EE1D98200820EEA /* SDL_vulkan_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D75171D1EE1D98200820EEA /* SDL_vulkan_internal.h */; };
 		4D7517201EE1D98200820EEA /* SDL_vulkan_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D75171E1EE1D98200820EEA /* SDL_vulkan_utils.c */; };
+		55FFA91A2122302B00D7CBED /* SDL_syspower.h in Headers */ = {isa = PBXBuildFile; fileRef = 55FFA9192122302B00D7CBED /* SDL_syspower.h */; };
 		566726451DF72CF5001DD3DB /* SDL_dataqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = 566726431DF72CF5001DD3DB /* SDL_dataqueue.c */; };
 		566726461DF72CF5001DD3DB /* SDL_dataqueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 566726441DF72CF5001DD3DB /* SDL_dataqueue.h */; };
 		56A6702E18565E450007D20F /* SDL_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 56A6702D18565E450007D20F /* SDL_internal.h */; };
@@ -110,8 +111,11 @@
 		56F9D5601DF73BA400C15B5D /* SDL_dataqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = 566726431DF72CF5001DD3DB /* SDL_dataqueue.c */; };
 		93CB792313FC5E5200BD3E05 /* SDL_uikitviewcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = 93CB792213FC5E5200BD3E05 /* SDL_uikitviewcontroller.h */; };
 		93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */; };
-		A7A9EEA91F702631002A5589 /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */; };
-		A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */; };
+		A704172E20F7E74800A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704172D20F7E74800A82227 /* controller_type.h */; };
+		A704172F20F7E76000A82227 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
+		A7C19D29212E552C00DF2152 /* SDL_displayevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C19D27212E552B00DF2152 /* SDL_displayevents_c.h */; };
+		A7C19D2A212E552C00DF2152 /* SDL_displayevents.c in Sources */ = {isa = PBXBuildFile; fileRef = A7C19D28212E552B00DF2152 /* SDL_displayevents.c */; };
+		A7C19D2B212E552C00DF2152 /* SDL_displayevents.c in Sources */ = {isa = PBXBuildFile; fileRef = A7C19D28212E552B00DF2152 /* SDL_displayevents.c */; };
 		A7F629241FE06523002F9CC9 /* SDL_uikitmetalview.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D7516F81EE1C28A00820EEA /* SDL_uikitmetalview.m */; };
 		AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
 		AA0AD06516647BD400CE5896 /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */; };
@@ -194,7 +198,30 @@
 		AADC5A631FDA10C800960936 /* SDL_shaders_metal_ios.h in Headers */ = {isa = PBXBuildFile; fileRef = AADC5A611FDA10C800960936 /* SDL_shaders_metal_ios.h */; };
 		AADC5A641FDA10C800960936 /* SDL_render_metal.m in Sources */ = {isa = PBXBuildFile; fileRef = AADC5A621FDA10C800960936 /* SDL_render_metal.m */; };
 		AADC5A651FDA10CB00960936 /* SDL_render_metal.m in Sources */ = {isa = PBXBuildFile; fileRef = AADC5A621FDA10C800960936 /* SDL_render_metal.m */; };
-		AAE7A4222041CCA90096E65A /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */; };
+		F30D9C99212CD0360047DF2E /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C98212CD0360047DF2E /* SDL_sensor.h */; };
+		F30D9C9E212CD0990047DF2E /* SDL_sensor_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C9B212CD0980047DF2E /* SDL_sensor_c.h */; };
+		F30D9C9F212CD0990047DF2E /* SDL_syssensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C9C212CD0990047DF2E /* SDL_syssensor.h */; };
+		F30D9CA0212CD0990047DF2E /* SDL_sensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C9D212CD0990047DF2E /* SDL_sensor.c */; };
+		F30D9CA1212CD0990047DF2E /* SDL_sensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C9D212CD0990047DF2E /* SDL_sensor.c */; };
+		F30D9CA5212CD0BF0047DF2E /* SDL_coremotionsensor.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */; };
+		F30D9CA6212CD0BF0047DF2E /* SDL_coremotionsensor.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */; };
+		F30D9CA7212CD0BF0047DF2E /* SDL_coremotionsensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9CA4212CD0BF0047DF2E /* SDL_coremotionsensor.h */; };
+		F30D9CC6212CE92C0047DF2E /* hid.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CC5212CE92C0047DF2E /* hid.m */; };
+		F30D9CC7212CE92C0047DF2E /* hid.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CC5212CE92C0047DF2E /* hid.m */; };
+		F36839CC214790950000F255 /* SDL_dummysensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F36839CA214790950000F255 /* SDL_dummysensor.h */; };
+		F36839CD214790950000F255 /* SDL_dummysensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F36839CB214790950000F255 /* SDL_dummysensor.c */; };
+		F36839CE214790950000F255 /* SDL_dummysensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F36839CB214790950000F255 /* SDL_dummysensor.c */; };
+		F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; };
+		F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; };
+		F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; };
+		F3BDD79520F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; };
+		F3BDD79620F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */; };
+		F3BDD79720F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */; };
+		F3BDD79820F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */; };
+		F3BDD79920F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */; };
+		F3BDD79B20F51CB8004ECBF3 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */; };
+		F3BDD79C20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */; };
+		F3BDD79D20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */; };
 		FA1DC2721C62BE65008F99A0 /* SDL_uikitclipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1DC2701C62BE65008F99A0 /* SDL_uikitclipboard.h */; };
 		FA1DC2731C62BE65008F99A0 /* SDL_uikitclipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = FA1DC2711C62BE65008F99A0 /* SDL_uikitclipboard.m */; };
 		FAB5981D1BB5C31500BE72C5 /* SDL_atomic.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8912E23B8D00BA343D /* SDL_atomic.c */; };
@@ -223,7 +250,6 @@
 		FAB5984C1BB5C31600BE72C5 /* SDL_syshaptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 047677B80EA76A31008ABAF1 /* SDL_syshaptic.c */; };
 		FAB5984D1BB5C31600BE72C5 /* SDL_haptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 047677B90EA76A31008ABAF1 /* SDL_haptic.c */; };
 		FAB598501BB5C31600BE72C5 /* SDL_sysjoystick.m in Sources */ = {isa = PBXBuildFile; fileRef = FD689F000E26E5B600F90B21 /* SDL_sysjoystick.m */; };
-		FAB598511BB5C31600BE72C5 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
 		FAB598521BB5C31600BE72C5 /* SDL_joystick.c in Sources */ = {isa = PBXBuildFile; fileRef = FD5F9D1E0E0E08B3008E885B /* SDL_joystick.c */; };
 		FAB598551BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */ = {isa = PBXBuildFile; fileRef = 047AF1B20EA98D6C00811173 /* SDL_sysloadso.c */; };
 		FAB598561BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */ = {isa = PBXBuildFile; fileRef = FD8BD8190E27E25900B52CD5 /* SDL_sysloadso.c */; };
@@ -380,21 +406,21 @@
 		0442EC4E12FE1C1E004C9285 /* SDL_render_sw_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_render_sw_c.h; sourceTree = "<group>"; };
 		0442EC4F12FE1C1E004C9285 /* SDL_render_sw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_sw.c; sourceTree = "<group>"; };
 		0442EC5212FE1C28004C9285 /* SDL_render_gles.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_gles.c; sourceTree = "<group>"; };
-		0442EC5412FE1C3F004C9285 /* SDL_hints.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_hints.c; path = ../../src/SDL_hints.c; sourceTree = SOURCE_ROOT; };
+		0442EC5412FE1C3F004C9285 /* SDL_hints.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hints.c; sourceTree = "<group>"; };
 		044E5FB711E606EB0076F181 /* SDL_clipboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_clipboard.c; sourceTree = "<group>"; };
 		0463873A0F0B5B7D0041FD65 /* SDL_blit_slow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_blit_slow.h; sourceTree = "<group>"; };
 		0463873E0F0B5B7D0041FD65 /* SDL_fillrect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_fillrect.c; sourceTree = "<group>"; };
 		047677B80EA76A31008ABAF1 /* SDL_syshaptic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_syshaptic.c; sourceTree = "<group>"; };
-		047677B90EA76A31008ABAF1 /* SDL_haptic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_haptic.c; path = ../../src/haptic/SDL_haptic.c; sourceTree = SOURCE_ROOT; };
-		047677BA0EA76A31008ABAF1 /* SDL_syshaptic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_syshaptic.h; path = ../../src/haptic/SDL_syshaptic.h; sourceTree = SOURCE_ROOT; };
+		047677B90EA76A31008ABAF1 /* SDL_haptic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_haptic.c; sourceTree = "<group>"; };
+		047677BA0EA76A31008ABAF1 /* SDL_syshaptic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syshaptic.h; sourceTree = "<group>"; };
 		047AF1B20EA98D6C00811173 /* SDL_sysloadso.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysloadso.c; sourceTree = "<group>"; };
 		04BA9D5F11EF474A00B60E01 /* SDL_gesture_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gesture_c.h; sourceTree = "<group>"; };
 		04BA9D6011EF474A00B60E01 /* SDL_gesture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gesture.c; sourceTree = "<group>"; };
 		04BA9D6111EF474A00B60E01 /* SDL_touch_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_touch_c.h; sourceTree = "<group>"; };
 		04BA9D6211EF474A00B60E01 /* SDL_touch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_touch.c; sourceTree = "<group>"; };
-		04BAC09A1300C1290055DE28 /* SDL_assert_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_assert_c.h; path = ../../src/SDL_assert_c.h; sourceTree = SOURCE_ROOT; };
-		04BAC09B1300C1290055DE28 /* SDL_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_log.c; path = ../../src/SDL_log.c; sourceTree = SOURCE_ROOT; };
-		04F2AF551104ABD200D6DDF7 /* SDL_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_assert.c; path = ../../src/SDL_assert.c; sourceTree = SOURCE_ROOT; };
+		04BAC09A1300C1290055DE28 /* SDL_assert_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_assert_c.h; sourceTree = "<group>"; };
+		04BAC09B1300C1290055DE28 /* SDL_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_log.c; sourceTree = "<group>"; };
+		04F2AF551104ABD200D6DDF7 /* SDL_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_assert.c; sourceTree = "<group>"; };
 		04F7806A12FB751400FC43C0 /* SDL_blendfillrect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_blendfillrect.c; sourceTree = "<group>"; };
 		04F7806B12FB751400FC43C0 /* SDL_blendfillrect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_blendfillrect.h; sourceTree = "<group>"; };
 		04F7806C12FB751400FC43C0 /* SDL_blendline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_blendline.c; sourceTree = "<group>"; };
@@ -417,23 +443,25 @@
 		4D7517191EE1D32200820EEA /* SDL_uikitmetalview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitmetalview.h; sourceTree = "<group>"; };
 		4D75171D1EE1D98200820EEA /* SDL_vulkan_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_vulkan_internal.h; sourceTree = "<group>"; };
 		4D75171E1EE1D98200820EEA /* SDL_vulkan_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_vulkan_utils.c; sourceTree = "<group>"; };
-		566726431DF72CF5001DD3DB /* SDL_dataqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_dataqueue.c; path = ../../src/SDL_dataqueue.c; sourceTree = "<group>"; };
-		566726441DF72CF5001DD3DB /* SDL_dataqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dataqueue.h; path = ../../src/SDL_dataqueue.h; sourceTree = "<group>"; };
-		56A6702D18565E450007D20F /* SDL_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_internal.h; path = ../../src/SDL_internal.h; sourceTree = "<group>"; };
-		56A6703118565E760007D20F /* SDL_dynapi_overrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dynapi_overrides.h; path = ../../src/dynapi/SDL_dynapi_overrides.h; sourceTree = "<group>"; };
-		56A6703218565E760007D20F /* SDL_dynapi_procs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dynapi_procs.h; path = ../../src/dynapi/SDL_dynapi_procs.h; sourceTree = "<group>"; };
-		56A6703318565E760007D20F /* SDL_dynapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_dynapi.c; path = ../../src/dynapi/SDL_dynapi.c; sourceTree = "<group>"; };
-		56A6703418565E760007D20F /* SDL_dynapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dynapi.h; path = ../../src/dynapi/SDL_dynapi.h; sourceTree = "<group>"; };
+		55FFA9192122302B00D7CBED /* SDL_syspower.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syspower.h; sourceTree = "<group>"; };
+		566726431DF72CF5001DD3DB /* SDL_dataqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dataqueue.c; sourceTree = "<group>"; };
+		566726441DF72CF5001DD3DB /* SDL_dataqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dataqueue.h; sourceTree = "<group>"; };
+		56A6702D18565E450007D20F /* SDL_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_internal.h; sourceTree = "<group>"; };
+		56A6703118565E760007D20F /* SDL_dynapi_overrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi_overrides.h; sourceTree = "<group>"; };
+		56A6703218565E760007D20F /* SDL_dynapi_procs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi_procs.h; sourceTree = "<group>"; };
+		56A6703318565E760007D20F /* SDL_dynapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dynapi.c; sourceTree = "<group>"; };
+		56A6703418565E760007D20F /* SDL_dynapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi.h; sourceTree = "<group>"; };
 		56C181DE17C44D5E00406AE3 /* SDL_filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_filesystem.h; sourceTree = "<group>"; };
-		56C181E117C44D7A00406AE3 /* SDL_sysfilesystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_sysfilesystem.m; path = ../../src/filesystem/cocoa/SDL_sysfilesystem.m; sourceTree = "<group>"; };
-		56EA86F913E9EC2B002E47EB /* SDL_coreaudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_coreaudio.m; path = coreaudio/SDL_coreaudio.m; sourceTree = "<group>"; };
-		56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_coreaudio.h; path = coreaudio/SDL_coreaudio.h; sourceTree = "<group>"; };
-		56ED04E0118A8EE200A56AA6 /* SDL_power.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_power.c; path = ../../src/power/SDL_power.c; sourceTree = SOURCE_ROOT; };
-		56ED04E2118A8EFD00A56AA6 /* SDL_syspower.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_syspower.m; path = ../../src/power/uikit/SDL_syspower.m; sourceTree = SOURCE_ROOT; };
+		56C181E117C44D7A00406AE3 /* SDL_sysfilesystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_sysfilesystem.m; path = cocoa/SDL_sysfilesystem.m; sourceTree = "<group>"; };
+		56EA86F913E9EC2B002E47EB /* SDL_coreaudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_coreaudio.m; sourceTree = "<group>"; };
+		56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_coreaudio.h; sourceTree = "<group>"; };
+		56ED04E0118A8EE200A56AA6 /* SDL_power.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_power.c; sourceTree = "<group>"; };
+		56ED04E2118A8EFD00A56AA6 /* SDL_syspower.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_syspower.m; sourceTree = "<group>"; };
 		93CB792213FC5E5200BD3E05 /* SDL_uikitviewcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitviewcontroller.h; sourceTree = "<group>"; };
 		93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitviewcontroller.m; sourceTree = "<group>"; };
-		A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_steamcontroller.c; sourceTree = "<group>"; };
-		A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steamcontroller.h; sourceTree = "<group>"; };
+		A704172D20F7E74800A82227 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = "<group>"; };
+		A7C19D27212E552B00DF2152 /* SDL_displayevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_displayevents_c.h; sourceTree = "<group>"; };
+		A7C19D28212E552B00DF2152 /* SDL_displayevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_displayevents.c; sourceTree = "<group>"; };
 		AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamecontroller.c; sourceTree = "<group>"; };
 		AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = "<group>"; };
 		AA0F8494178D5F1A00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = "<group>"; };
@@ -510,6 +538,21 @@
 		AADA5B8E16CCAB7C00107CF7 /* SDL_bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_bits.h; sourceTree = "<group>"; };
 		AADC5A611FDA10C800960936 /* SDL_shaders_metal_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_metal_ios.h; sourceTree = "<group>"; };
 		AADC5A621FDA10C800960936 /* SDL_render_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_render_metal.m; sourceTree = "<group>"; };
+		F30D9C98212CD0360047DF2E /* SDL_sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sensor.h; sourceTree = "<group>"; };
+		F30D9C9B212CD0980047DF2E /* SDL_sensor_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sensor_c.h; sourceTree = "<group>"; };
+		F30D9C9C212CD0990047DF2E /* SDL_syssensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syssensor.h; sourceTree = "<group>"; };
+		F30D9C9D212CD0990047DF2E /* SDL_sensor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sensor.c; sourceTree = "<group>"; };
+		F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_coremotionsensor.m; sourceTree = "<group>"; };
+		F30D9CA4212CD0BF0047DF2E /* SDL_coremotionsensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_coremotionsensor.h; sourceTree = "<group>"; };
+		F30D9CC5212CE92C0047DF2E /* hid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = hid.m; sourceTree = "<group>"; };
+		F36839CA214790950000F255 /* SDL_dummysensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dummysensor.h; sourceTree = "<group>"; };
+		F36839CB214790950000F255 /* SDL_dummysensor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dummysensor.c; sourceTree = "<group>"; };
+		F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = "<group>"; };
+		F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = "<group>"; };
+		F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = "<group>"; };
+		F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps4.c; sourceTree = "<group>"; };
+		F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapijoystick_c.h; sourceTree = "<group>"; };
+		F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapijoystick.c; sourceTree = "<group>"; };
 		FA1DC2701C62BE65008F99A0 /* SDL_uikitclipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitclipboard.h; sourceTree = "<group>"; };
 		FA1DC2711C62BE65008F99A0 /* SDL_uikitclipboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitclipboard.m; sourceTree = "<group>"; };
 		FAB598141BB5C1B100BE72C5 /* libSDL2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDL2.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -567,9 +610,9 @@
 		FD99B99B0DD52EDC00FB1D6B /* SDL_windowevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_windowevents.c; sourceTree = "<group>"; };
 		FD99B99C0DD52EDC00FB1D6B /* SDL_windowevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_windowevents_c.h; sourceTree = "<group>"; };
 		FD99B99E0DD52EDC00FB1D6B /* SDL_rwops.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rwops.c; sourceTree = "<group>"; };
-		FD99B9D40DD52EDC00FB1D6B /* SDL_error_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_error_c.h; path = ../../src/SDL_error_c.h; sourceTree = "<group>"; };
-		FD99B9D50DD52EDC00FB1D6B /* SDL_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_error.c; path = ../../src/SDL_error.c; sourceTree = "<group>"; };
-		FD99B9D80DD52EDC00FB1D6B /* SDL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL.c; path = ../../src/SDL.c; sourceTree = "<group>"; };
+		FD99B9D40DD52EDC00FB1D6B /* SDL_error_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_error_c.h; sourceTree = "<group>"; };
+		FD99B9D50DD52EDC00FB1D6B /* SDL_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_error.c; sourceTree = "<group>"; };
+		FD99B9D80DD52EDC00FB1D6B /* SDL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL.c; sourceTree = "<group>"; };
 		FD99BA070DD52EDC00FB1D6B /* SDL_syscond.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_syscond.c; sourceTree = "<group>"; };
 		FD99BA080DD52EDC00FB1D6B /* SDL_sysmutex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysmutex.c; sourceTree = "<group>"; };
 		FD99BA090DD52EDC00FB1D6B /* SDL_sysmutex_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysmutex_c.h; sourceTree = "<group>"; };
@@ -642,9 +685,8 @@
 				04409BA412FA989600FB9AA8 /* SDL_yuv_sw_c.h */,
 				04409BA512FA989600FB9AA8 /* SDL_yuv_sw.c */,
 			);
-			name = render;
-			path = ../../src/render;
-			sourceTree = SOURCE_ROOT;
+			path = render;
+			sourceTree = "<group>";
 		};
 		041B2CE812FA0F680087D585 /* opengles */ = {
 			isa = PBXGroup;
@@ -683,7 +725,7 @@
 				047677B90EA76A31008ABAF1 /* SDL_haptic.c */,
 				047677BA0EA76A31008ABAF1 /* SDL_syshaptic.h */,
 			);
-			name = haptic;
+			path = haptic;
 			sourceTree = "<group>";
 		};
 		047677B70EA76A31008ABAF1 /* dummy */ = {
@@ -691,9 +733,8 @@
 			children = (
 				047677B80EA76A31008ABAF1 /* SDL_syshaptic.c */,
 			);
-			name = dummy;
-			path = ../../src/haptic/dummy;
-			sourceTree = SOURCE_ROOT;
+			path = dummy;
+			sourceTree = "<group>";
 		};
 		047AF1B10EA98D6C00811173 /* dummy */ = {
 			isa = PBXGroup;
@@ -709,9 +750,8 @@
 				04FFAB8912E23B8D00BA343D /* SDL_atomic.c */,
 				04FFAB8A12E23B8D00BA343D /* SDL_spinlock.c */,
 			);
-			name = atomic;
-			path = ../../src/atomic;
-			sourceTree = SOURCE_ROOT;
+			path = atomic;
+			sourceTree = "<group>";
 		};
 		19C28FACFE9D520D11CA2CBB /* Products */ = {
 			isa = PBXGroup;
@@ -741,7 +781,7 @@
 				56A6703318565E760007D20F /* SDL_dynapi.c */,
 				56A6703418565E760007D20F /* SDL_dynapi.h */,
 			);
-			name = dynapi;
+			path = dynapi;
 			sourceTree = "<group>";
 		};
 		56C181E017C44D6900406AE3 /* filesystem */ = {
@@ -749,7 +789,7 @@
 			children = (
 				56C181E117C44D7A00406AE3 /* SDL_sysfilesystem.m */,
 			);
-			name = filesystem;
+			path = filesystem;
 			sourceTree = "<group>";
 		};
 		56EA86F813E9EBF9002E47EB /* coreaudio */ = {
@@ -758,7 +798,7 @@
 				56EA86F913E9EC2B002E47EB /* SDL_coreaudio.m */,
 				56EA86FA13E9EC2B002E47EB /* SDL_coreaudio.h */,
 			);
-			name = coreaudio;
+			path = coreaudio;
 			sourceTree = "<group>";
 		};
 		56ED04DE118A8E9A00A56AA6 /* power */ = {
@@ -767,24 +807,16 @@
 				56ED04E0118A8EE200A56AA6 /* SDL_power.c */,
 				56ED04DF118A8EB700A56AA6 /* uikit */,
 			);
-			name = power;
+			path = power;
 			sourceTree = "<group>";
 		};
 		56ED04DF118A8EB700A56AA6 /* uikit */ = {
 			isa = PBXGroup;
 			children = (
+				55FFA9192122302B00D7CBED /* SDL_syspower.h */,
 				56ED04E2118A8EFD00A56AA6 /* SDL_syspower.m */,
 			);
-			name = uikit;
-			sourceTree = "<group>";
-		};
-		A7A9EEA61F702607002A5589 /* steam */ = {
-			isa = PBXGroup;
-			children = (
-				A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */,
-				A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */,
-			);
-			path = steam;
+			path = uikit;
 			sourceTree = "<group>";
 		};
 		AA13B3521FB8B41700D9FEE6 /* yuv2rgb */ = {
@@ -807,6 +839,65 @@
 			path = metal;
 			sourceTree = "<group>";
 		};
+		F30D9C9A212CD0590047DF2E /* sensor */ = {
+			isa = PBXGroup;
+			children = (
+				F30D9CA2212CD09E0047DF2E /* coremotion */,
+				F36839C9214790740000F255 /* dummy */,
+				F30D9C9B212CD0980047DF2E /* SDL_sensor_c.h */,
+				F30D9C9D212CD0990047DF2E /* SDL_sensor.c */,
+				F30D9C9C212CD0990047DF2E /* SDL_syssensor.h */,
+			);
+			path = sensor;
+			sourceTree = "<group>";
+		};
+		F30D9CA2212CD09E0047DF2E /* coremotion */ = {
+			isa = PBXGroup;
+			children = (
+				F30D9CA4212CD0BF0047DF2E /* SDL_coremotionsensor.h */,
+				F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */,
+			);
+			path = coremotion;
+			sourceTree = "<group>";
+		};
+		F35CEA6E20F51B7F003ECE98 /* hidapi */ = {
+			isa = PBXGroup;
+			children = (
+				F3BDD77420F51C18004ECBF3 /* ios */,
+			);
+			path = hidapi;
+			sourceTree = "<group>";
+		};
+		F36839C9214790740000F255 /* dummy */ = {
+			isa = PBXGroup;
+			children = (
+				F36839CB214790950000F255 /* SDL_dummysensor.c */,
+				F36839CA214790950000F255 /* SDL_dummysensor.h */,
+			);
+			path = dummy;
+			sourceTree = "<group>";
+		};
+		F3BDD77420F51C18004ECBF3 /* ios */ = {
+			isa = PBXGroup;
+			children = (
+				F30D9CC5212CE92C0047DF2E /* hid.m */,
+			);
+			path = ios;
+			sourceTree = "<group>";
+		};
+		F3BDD78A20F51C8D004ECBF3 /* hidapi */ = {
+			isa = PBXGroup;
+			children = (
+				F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */,
+				F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */,
+				F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */,
+				F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */,
+				F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */,
+				F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */,
+			);
+			path = hidapi;
+			sourceTree = "<group>";
+		};
 		FD3F4A6F0DEA620800C5B771 /* stdlib */ = {
 			isa = PBXGroup;
 			children = (
@@ -817,23 +908,22 @@
 				FD3F4A740DEA620800C5B771 /* SDL_stdlib.c */,
 				FD3F4A750DEA620800C5B771 /* SDL_string.c */,
 			);
-			name = stdlib;
-			path = ../../src/stdlib;
-			sourceTree = SOURCE_ROOT;
+			path = stdlib;
+			sourceTree = "<group>";
 		};
 		FD5F9D080E0E08B3008E885B /* joystick */ = {
 			isa = PBXGroup;
 			children = (
-				A7A9EEA61F702607002A5589 /* steam */,
+				F3BDD78A20F51C8D004ECBF3 /* hidapi */,
 				FD689EFF0E26E5B600F90B21 /* iphoneos */,
+				A704172D20F7E74800A82227 /* controller_type.h */,
 				AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */,
 				FD5F9D1E0E0E08B3008E885B /* SDL_joystick.c */,
 				FD5F9D1F0E0E08B3008E885B /* SDL_joystick_c.h */,
 				FD5F9D200E0E08B3008E885B /* SDL_sysjoystick.h */,
 			);
-			name = joystick;
-			path = ../../src/joystick;
-			sourceTree = SOURCE_ROOT;
+			path = joystick;
+			sourceTree = "<group>";
 		};
 		FD689EFF0E26E5B600F90B21 /* iphoneos */ = {
 			isa = PBXGroup;
@@ -884,9 +974,8 @@
 				047AF1B10EA98D6C00811173 /* dummy */,
 				FD8BD8180E27E25900B52CD5 /* dlopen */,
 			);
-			name = loadso;
-			path = ../../src/loadso;
-			sourceTree = SOURCE_ROOT;
+			path = loadso;
+			sourceTree = "<group>";
 		};
 		FD8BD8180E27E25900B52CD5 /* dlopen */ = {
 			isa = PBXGroup;
@@ -942,6 +1031,7 @@
 				AA7558891595D55500BBD41B /* SDL_revision.h */,
 				AA75588A1595D55500BBD41B /* SDL_rwops.h */,
 				AA75588B1595D55500BBD41B /* SDL_scancode.h */,
+				F30D9C98212CD0360047DF2E /* SDL_sensor.h */,
 				AA75588C1595D55500BBD41B /* SDL_shape.h */,
 				AA75588D1595D55500BBD41B /* SDL_stdinc.h */,
 				AA75588E1595D55500BBD41B /* SDL_surface.h */,
@@ -970,10 +1060,12 @@
 				FD99B99D0DD52EDC00FB1D6B /* file */,
 				56C181E017C44D6900406AE3 /* filesystem */,
 				047677B60EA769DF008ABAF1 /* haptic */,
+				F35CEA6E20F51B7F003ECE98 /* hidapi */,
 				FD5F9D080E0E08B3008E885B /* joystick */,
 				FD8BD8150E27E25900B52CD5 /* loadso */,
 				56ED04DE118A8E9A00A56AA6 /* power */,
 				041B2CE312FA0F680087D585 /* render */,
+				F30D9C9A212CD0590047DF2E /* sensor */,
 				FD3F4A6F0DEA620800C5B771 /* stdlib */,
 				FD99B9E00DD52EDC00FB1D6B /* thread */,
 				FD99BA1E0DD52EDC00FB1D6B /* timer */,
@@ -990,6 +1082,7 @@
 				FD99B9D80DD52EDC00FB1D6B /* SDL.c */,
 			);
 			name = "Library Source";
+			path = ../../src;
 			sourceTree = "<group>";
 		};
 		FD99B8FB0DD52EDC00FB1D6B /* audio */ = {
@@ -1006,8 +1099,7 @@
 				FD99B9530DD52EDC00FB1D6B /* SDL_wave.c */,
 				FD99B9540DD52EDC00FB1D6B /* SDL_wave.h */,
 			);
-			name = audio;
-			path = ../../src/audio;
+			path = audio;
 			sourceTree = "<group>";
 		};
 		FD99B91C0DD52EDC00FB1D6B /* dummy */ = {
@@ -1024,8 +1116,7 @@
 			children = (
 				FD99B98B0DD52EDC00FB1D6B /* SDL_cpuinfo.c */,
 			);
-			name = cpuinfo;
-			path = ../../src/cpuinfo;
+			path = cpuinfo;
 			sourceTree = "<group>";
 		};
 		FD99B98C0DD52EDC00FB1D6B /* events */ = {
@@ -1038,6 +1129,8 @@
 				FD99B9920DD52EDC00FB1D6B /* scancodes_xfree86.h */,
 				0420496F11E6F03D007E7EC9 /* SDL_clipboardevents.c */,
 				0420496E11E6F03D007E7EC9 /* SDL_clipboardevents_c.h */,
+				A7C19D27212E552B00DF2152 /* SDL_displayevents_c.h */,
+				A7C19D28212E552B00DF2152 /* SDL_displayevents.c */,
 				AA704DD5162AA90A0076D1C1 /* SDL_dropevents.c */,
 				AA704DD4162AA90A0076D1C1 /* SDL_dropevents_c.h */,
 				FD99B9930DD52EDC00FB1D6B /* SDL_events.c */,
@@ -1055,8 +1148,7 @@
 				FD99B99B0DD52EDC00FB1D6B /* SDL_windowevents.c */,
 				FD99B99C0DD52EDC00FB1D6B /* SDL_windowevents_c.h */,
 			);
-			name = events;
-			path = ../../src/events;
+			path = events;
 			sourceTree = "<group>";
 		};
 		FD99B99D0DD52EDC00FB1D6B /* file */ = {
@@ -1065,8 +1157,7 @@
 				006E9885119552DD001DE610 /* cocoa */,
 				FD99B99E0DD52EDC00FB1D6B /* SDL_rwops.c */,
 			);
-			name = file;
-			path = ../../src/file;
+			path = file;
 			sourceTree = "<group>";
 		};
 		FD99B9E00DD52EDC00FB1D6B /* thread */ = {
@@ -1077,8 +1168,7 @@
 				FD99BA150DD52EDC00FB1D6B /* SDL_thread.c */,
 				FD99BA160DD52EDC00FB1D6B /* SDL_thread_c.h */,
 			);
-			name = thread;
-			path = ../../src/thread;
+			path = thread;
 			sourceTree = "<group>";
 		};
 		FD99BA060DD52EDC00FB1D6B /* pthread */ = {
@@ -1102,8 +1192,7 @@
 				FD99BA2E0DD52EDC00FB1D6B /* SDL_timer.c */,
 				FD99BA2F0DD52EDC00FB1D6B /* SDL_timer_c.h */,
 			);
-			name = timer;
-			path = ../../src/timer;
+			path = timer;
 			sourceTree = "<group>";
 		};
 		FD99BA300DD52EDC00FB1D6B /* unix */ = {
@@ -1154,9 +1243,8 @@
 				AA13B34F1FB8B3CC00D9FEE6 /* SDL_yuv.c */,
 				AA13B3481FB8B27800D9FEE6 /* SDL_yuv_c.h */,
 			);
-			name = video;
-			path = ../../src/video;
-			sourceTree = SOURCE_ROOT;
+			path = video;
+			sourceTree = "<group>";
 		};
 		FDA685F40DF244C800F98A1A /* dummy */ = {
 			isa = PBXGroup;
@@ -1213,13 +1301,13 @@
 				AA13B3591FB8B46400D9FEE6 /* yuv_rgb.h in Headers */,
 				04F7807712FB751400FC43C0 /* SDL_blendfillrect.h in Headers */,
 				04F7807912FB751400FC43C0 /* SDL_blendline.h in Headers */,
+				F3BDD79B20F51CB8004ECBF3 /* SDL_hidapijoystick_c.h in Headers */,
 				04F7807B12FB751400FC43C0 /* SDL_blendpoint.h in Headers */,
 				04F7807C12FB751400FC43C0 /* SDL_draw.h in Headers */,
 				04F7807E12FB751400FC43C0 /* SDL_drawline.h in Headers */,
 				AA13B34E1FB8B27800D9FEE6 /* SDL_yuv_c.h in Headers */,
 				04F7808012FB751400FC43C0 /* SDL_drawpoint.h in Headers */,
 				04F7808412FB753F00FC43C0 /* SDL_nullframebuffer_c.h in Headers */,
-				A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */,
 				0442EC5012FE1C1E004C9285 /* SDL_render_sw_c.h in Headers */,
 				FA1DC2721C62BE65008F99A0 /* SDL_uikitclipboard.h in Headers */,
 				0402A85A12FE70C600CECEE3 /* SDL_shaders_gles2.h in Headers */,
@@ -1232,7 +1320,9 @@
 				AA75589A1595D55500BBD41B /* SDL_assert.h in Headers */,
 				AA75589B1595D55500BBD41B /* SDL_atomic.h in Headers */,
 				AA75589C1595D55500BBD41B /* SDL_audio.h in Headers */,
+				55FFA91A2122302B00D7CBED /* SDL_syspower.h in Headers */,
 				AA75589D1595D55500BBD41B /* SDL_blendmode.h in Headers */,
+				F30D9C9E212CD0990047DF2E /* SDL_sensor_c.h in Headers */,
 				AA75589E1595D55500BBD41B /* SDL_clipboard.h in Headers */,
 				AA75589F1595D55500BBD41B /* SDL_config_iphoneos.h in Headers */,
 				AA7558A01595D55500BBD41B /* SDL_config.h in Headers */,
@@ -1247,14 +1337,18 @@
 				AA7558A71595D55500BBD41B /* SDL_haptic.h in Headers */,
 				AA7558A81595D55500BBD41B /* SDL_hints.h in Headers */,
 				566726461DF72CF5001DD3DB /* SDL_dataqueue.h in Headers */,
+				F30D9C9F212CD0990047DF2E /* SDL_syssensor.h in Headers */,
 				AA7558AA1595D55500BBD41B /* SDL_joystick.h in Headers */,
 				AA13B34B1FB8B27800D9FEE6 /* SDL_shape_internals.h in Headers */,
 				AA7558AB1595D55500BBD41B /* SDL_keyboard.h in Headers */,
+				A704172E20F7E74800A82227 /* controller_type.h in Headers */,
 				AA7558AC1595D55500BBD41B /* SDL_keycode.h in Headers */,
 				AA7558AD1595D55500BBD41B /* SDL_loadso.h in Headers */,
 				AA7558AE1595D55500BBD41B /* SDL_log.h in Headers */,
+				F30D9CA7212CD0BF0047DF2E /* SDL_coremotionsensor.h in Headers */,
 				AA7558AF1595D55500BBD41B /* SDL_main.h in Headers */,
 				AA7558B01595D55500BBD41B /* SDL_mouse.h in Headers */,
+				A7C19D29212E552C00DF2152 /* SDL_displayevents_c.h in Headers */,
 				AA7558B11595D55500BBD41B /* SDL_mutex.h in Headers */,
 				AA7558B21595D55500BBD41B /* SDL_name.h in Headers */,
 				AA7558B31595D55500BBD41B /* SDL_opengl.h in Headers */,
@@ -1275,6 +1369,7 @@
 				FAD4F7021BA3C4E8008346CE /* SDL_sysjoystick_c.h in Headers */,
 				AA7558C11595D55500BBD41B /* SDL_surface.h in Headers */,
 				AA7558C21595D55500BBD41B /* SDL_system.h in Headers */,
+				F30D9C99212CD0360047DF2E /* SDL_sensor.h in Headers */,
 				AA7558C31595D55500BBD41B /* SDL_syswm.h in Headers */,
 				AA7558C41595D55500BBD41B /* SDL_thread.h in Headers */,
 				AA7558C51595D55500BBD41B /* SDL_timer.h in Headers */,
@@ -1290,6 +1385,7 @@
 				AA9FF9511637C6E5000DF050 /* SDL_messagebox.h in Headers */,
 				AABCC3941640643D00AB8930 /* SDL_uikitmessagebox.h in Headers */,
 				AA0AD06516647BD400CE5896 /* SDL_gamecontroller.h in Headers */,
+				F36839CC214790950000F255 /* SDL_dummysensor.h in Headers */,
 				AADA5B8F16CCAB7C00107CF7 /* SDL_bits.h in Headers */,
 				56C181DF17C44D5E00406AE3 /* SDL_filesystem.h in Headers */,
 			);
@@ -1432,6 +1528,7 @@
 				FAB598251BB5C31500BE72C5 /* SDL_audiocvt.c in Sources */,
 				FAB598271BB5C31500BE72C5 /* SDL_audiotypecvt.c in Sources */,
 				FAB598281BB5C31500BE72C5 /* SDL_mixer.c in Sources */,
+				F3BDD79720F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */,
 				FAB5982A1BB5C31500BE72C5 /* SDL_wave.c in Sources */,
 				FAFDF8C61D88D4530083E6F2 /* SDL_uikitclipboard.m in Sources */,
 				FAB5982C1BB5C31500BE72C5 /* SDL_cpuinfo.c in Sources */,
@@ -1442,10 +1539,13 @@
 				A7F629241FE06523002F9CC9 /* SDL_uikitmetalview.m in Sources */,
 				FAB5983C1BB5C31500BE72C5 /* SDL_gesture.c in Sources */,
 				FAB5983E1BB5C31500BE72C5 /* SDL_keyboard.c in Sources */,
+				F3BDD79520F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */,
 				FAB598401BB5C31500BE72C5 /* SDL_mouse.c in Sources */,
+				A704172F20F7E76000A82227 /* SDL_gamecontroller.c in Sources */,
 				FAB598421BB5C31500BE72C5 /* SDL_quit.c in Sources */,
 				FAB598441BB5C31500BE72C5 /* SDL_touch.c in Sources */,
 				FAB598461BB5C31500BE72C5 /* SDL_windowevents.c in Sources */,
+				F30D9CC7212CE92C0047DF2E /* hid.m in Sources */,
 				FAB598491BB5C31600BE72C5 /* SDL_rwopsbundlesupport.m in Sources */,
 				FAB5984A1BB5C31600BE72C5 /* SDL_rwops.c in Sources */,
 				FAB5984B1BB5C31600BE72C5 /* SDL_sysfilesystem.m in Sources */,
@@ -1454,16 +1554,18 @@
 				AADC5A5F1FDA105600960936 /* SDL_vulkan_utils.c in Sources */,
 				AADC5A5E1FDA105300960936 /* SDL_yuv.c in Sources */,
 				FAB5984D1BB5C31600BE72C5 /* SDL_haptic.c in Sources */,
+				F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */,
 				FAB598501BB5C31600BE72C5 /* SDL_sysjoystick.m in Sources */,
-				FAB598511BB5C31600BE72C5 /* SDL_gamecontroller.c in Sources */,
 				FAB598521BB5C31600BE72C5 /* SDL_joystick.c in Sources */,
 				FAB598551BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */,
 				AADC5A651FDA10CB00960936 /* SDL_render_metal.m in Sources */,
 				FAB598561BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */,
 				FAB598571BB5C31600BE72C5 /* SDL_power.c in Sources */,
+				F30D9CA1212CD0990047DF2E /* SDL_sensor.c in Sources */,
 				FAB598581BB5C31600BE72C5 /* SDL_syspower.m in Sources */,
 				56F9D5601DF73BA400C15B5D /* SDL_dataqueue.c in Sources */,
 				FAB598591BB5C31600BE72C5 /* SDL_render_gles.c in Sources */,
+				F30D9CA6212CD0BF0047DF2E /* SDL_coremotionsensor.m in Sources */,
 				FAB5985A1BB5C31600BE72C5 /* SDL_render_gles2.c in Sources */,
 				FAB5985B1BB5C31600BE72C5 /* SDL_shaders_gles2.c in Sources */,
 				FAB5985D1BB5C31600BE72C5 /* SDL_blendfillrect.c in Sources */,
@@ -1479,19 +1581,22 @@
 				FAB598731BB5C31600BE72C5 /* SDL_iconv.c in Sources */,
 				FAB598741BB5C31600BE72C5 /* SDL_malloc.c in Sources */,
 				FAB598751BB5C31600BE72C5 /* SDL_qsort.c in Sources */,
+				F36839CE214790950000F255 /* SDL_dummysensor.c in Sources */,
+				A7C19D2B212E552C00DF2152 /* SDL_displayevents.c in Sources */,
 				FAB598761BB5C31600BE72C5 /* SDL_stdlib.c in Sources */,
 				FAB598771BB5C31600BE72C5 /* SDL_string.c in Sources */,
 				FAB598781BB5C31600BE72C5 /* SDL_syscond.c in Sources */,
+				F3BDD79D20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */,
 				AADC5A601FDA10A400960936 /* SDL_uikitvulkan.m in Sources */,
 				FAB598791BB5C31600BE72C5 /* SDL_sysmutex.c in Sources */,
 				FAB5987B1BB5C31600BE72C5 /* SDL_syssem.c in Sources */,
 				FAB5987C1BB5C31600BE72C5 /* SDL_systhread.c in Sources */,
-				AAE7A4222041CCA90096E65A /* SDL_steamcontroller.c in Sources */,
 				FAB5987E1BB5C31600BE72C5 /* SDL_systls.c in Sources */,
 				FAB598801BB5C31600BE72C5 /* SDL_thread.c in Sources */,
 				FAB598821BB5C31600BE72C5 /* SDL_systimer.c in Sources */,
 				FAB598831BB5C31600BE72C5 /* SDL_timer.c in Sources */,
 				FAB598871BB5C31600BE72C5 /* SDL_uikitappdelegate.m in Sources */,
+				F3BDD79920F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */,
 				FAB598891BB5C31600BE72C5 /* SDL_uikitevents.m in Sources */,
 				FAB5988B1BB5C31600BE72C5 /* SDL_uikitmessagebox.m in Sources */,
 				FAB5988D1BB5C31600BE72C5 /* SDL_uikitmodes.m in Sources */,
@@ -1535,7 +1640,7 @@
 			files = (
 				FD6526810DE8FCDD002AD96B /* SDL_systimer.c in Sources */,
 				FD6526800DE8FCDD002AD96B /* SDL_timer.c in Sources */,
-				A7A9EEA91F702631002A5589 /* SDL_steamcontroller.c in Sources */,
+				F30D9CA5212CD0BF0047DF2E /* SDL_coremotionsensor.m in Sources */,
 				FD3F4A7B0DEA620800C5B771 /* SDL_string.c in Sources */,
 				FD6526660DE8FCDD002AD96B /* SDL_dummyaudio.c in Sources */,
 				FD6526670DE8FCDD002AD96B /* SDL_audio.c in Sources */,
@@ -1554,6 +1659,7 @@
 				FD6526750DE8FCDD002AD96B /* SDL_windowevents.c in Sources */,
 				4D7516FB1EE1C28A00820EEA /* SDL_uikitmetalview.m in Sources */,
 				FD6526760DE8FCDD002AD96B /* SDL_rwops.c in Sources */,
+				F30D9CC6212CE92C0047DF2E /* hid.m in Sources */,
 				4D7517201EE1D98200820EEA /* SDL_vulkan_utils.c in Sources */,
 				FD6526780DE8FCDD002AD96B /* SDL_error.c in Sources */,
 				FD65267A0DE8FCDD002AD96B /* SDL.c in Sources */,
@@ -1566,7 +1672,9 @@
 				FD3F4A760DEA620800C5B771 /* SDL_getenv.c in Sources */,
 				FD3F4A770DEA620800C5B771 /* SDL_iconv.c in Sources */,
 				FD3F4A780DEA620800C5B771 /* SDL_malloc.c in Sources */,
+				F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */,
 				FD3F4A790DEA620800C5B771 /* SDL_qsort.c in Sources */,
+				F3BDD79820F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */,
 				FD3F4A7A0DEA620800C5B771 /* SDL_stdlib.c in Sources */,
 				FDA6844D0DF2374E00F98A1A /* SDL_blit.c in Sources */,
 				FDA6844F0DF2374E00F98A1A /* SDL_blit_0.c in Sources */,
@@ -1595,15 +1703,18 @@
 				FD689F1F0E26E5D900F90B21 /* SDL_uikitopengles.m in Sources */,
 				FD689F210E26E5D900F90B21 /* SDL_uikitvideo.m in Sources */,
 				FD689F230E26E5D900F90B21 /* SDL_uikitview.m in Sources */,
+				A7C19D2A212E552C00DF2152 /* SDL_displayevents.c in Sources */,
 				FD689F250E26E5D900F90B21 /* SDL_uikitwindow.m in Sources */,
 				FD689F270E26E5D900F90B21 /* SDL_uikitopenglview.m in Sources */,
 				FD689FCE0E26E9D400F90B21 /* SDL_uikitappdelegate.m in Sources */,
 				FD8BD8250E27E25900B52CD5 /* SDL_sysloadso.c in Sources */,
+				F3BDD79C20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */,
 				047677BB0EA76A31008ABAF1 /* SDL_syshaptic.c in Sources */,
 				047677BC0EA76A31008ABAF1 /* SDL_haptic.c in Sources */,
 				047AF1B30EA98D6C00811173 /* SDL_sysloadso.c in Sources */,
 				046387460F0B5B7D0041FD65 /* SDL_fillrect.c in Sources */,
 				04F2AF561104ABD200D6DDF7 /* SDL_assert.c in Sources */,
+				F3BDD79620F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */,
 				56ED04E1118A8EE200A56AA6 /* SDL_power.c in Sources */,
 				56ED04E3118A8EFD00A56AA6 /* SDL_syspower.m in Sources */,
 				006E9889119552DD001DE610 /* SDL_rwopsbundlesupport.m in Sources */,
@@ -1626,9 +1737,12 @@
 				0442EC5512FE1C3F004C9285 /* SDL_hints.c in Sources */,
 				AA13B34A1FB8B27800D9FEE6 /* SDL_shape.c in Sources */,
 				0402A85812FE70C600CECEE3 /* SDL_render_gles2.c in Sources */,
+				F36839CD214790950000F255 /* SDL_dummysensor.c in Sources */,
 				0402A85912FE70C600CECEE3 /* SDL_shaders_gles2.c in Sources */,
 				04BAC09D1300C1290055DE28 /* SDL_log.c in Sources */,
 				56EA86FB13E9EC2B002E47EB /* SDL_coreaudio.m in Sources */,
+				F30D9CA0212CD0990047DF2E /* SDL_sensor.c in Sources */,
+				F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */,
 				93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */,
 				AA628ADB159369E3005138DD /* SDL_rotate.c in Sources */,
 				AA126AD51617C5E7005ABC8F /* SDL_uikitmodes.m in Sources */,
@@ -1859,6 +1973,7 @@
 		FD6526640DE8FCCB002AD96B /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ENABLE_OBJC_ARC = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
@@ -1875,6 +1990,7 @@
 		FD6526650DE8FCCB002AD96B /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ENABLE_OBJC_ARC = YES;
 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
diff --git a/source/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj b/source/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj
index 89e381e..8a6ef37 100644
--- a/source/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj
+++ b/source/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj
@@ -53,22 +53,22 @@
 
 /* Begin PBXFileReference section */
 		AA1EE4461760589B0029C7A5 /* libSDL2test.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDL2test.a; sourceTree = BUILT_PRODUCTS_DIR; };
-		AA1EE454176059AB0029C7A5 /* SDL_test_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_common.c; path = ../../src/test/SDL_test_common.c; sourceTree = "<group>"; };
-		AA1EE455176059AB0029C7A5 /* SDL_test_compare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_compare.c; path = ../../src/test/SDL_test_compare.c; sourceTree = "<group>"; };
-		AA1EE456176059AB0029C7A5 /* SDL_test_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_crc32.c; path = ../../src/test/SDL_test_crc32.c; sourceTree = "<group>"; };
-		AA1EE457176059AB0029C7A5 /* SDL_test_font.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_font.c; path = ../../src/test/SDL_test_font.c; sourceTree = "<group>"; };
-		AA1EE458176059AB0029C7A5 /* SDL_test_fuzzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_fuzzer.c; path = ../../src/test/SDL_test_fuzzer.c; sourceTree = "<group>"; };
-		AA1EE459176059AB0029C7A5 /* SDL_test_harness.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_harness.c; path = ../../src/test/SDL_test_harness.c; sourceTree = "<group>"; };
-		AA1EE45A176059AB0029C7A5 /* SDL_test_imageBlit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imageBlit.c; path = ../../src/test/SDL_test_imageBlit.c; sourceTree = "<group>"; };
-		AA1EE45B176059AB0029C7A5 /* SDL_test_imageBlitBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imageBlitBlend.c; path = ../../src/test/SDL_test_imageBlitBlend.c; sourceTree = "<group>"; };
-		AA1EE45C176059AB0029C7A5 /* SDL_test_imageFace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imageFace.c; path = ../../src/test/SDL_test_imageFace.c; sourceTree = "<group>"; };
-		AA1EE45D176059AB0029C7A5 /* SDL_test_imagePrimitives.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imagePrimitives.c; path = ../../src/test/SDL_test_imagePrimitives.c; sourceTree = "<group>"; };
-		AA1EE45E176059AB0029C7A5 /* SDL_test_imagePrimitivesBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imagePrimitivesBlend.c; path = ../../src/test/SDL_test_imagePrimitivesBlend.c; sourceTree = "<group>"; };
-		AA1EE45F176059AB0029C7A5 /* SDL_test_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_log.c; path = ../../src/test/SDL_test_log.c; sourceTree = "<group>"; };
-		AA1EE460176059AB0029C7A5 /* SDL_test_md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_md5.c; path = ../../src/test/SDL_test_md5.c; sourceTree = "<group>"; };
-		AA1EE461176059AB0029C7A5 /* SDL_test_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_random.c; path = ../../src/test/SDL_test_random.c; sourceTree = "<group>"; };
-		AAF02FFF1F9009B100B9A9FB /* SDL_test_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_memory.c; path = ../../src/test/SDL_test_memory.c; sourceTree = "<group>"; };
-		AAF030001F9009B100B9A9FB /* SDL_test_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_assert.c; path = ../../src/test/SDL_test_assert.c; sourceTree = "<group>"; };
+		AA1EE454176059AB0029C7A5 /* SDL_test_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_common.c; sourceTree = "<group>"; };
+		AA1EE455176059AB0029C7A5 /* SDL_test_compare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_compare.c; sourceTree = "<group>"; };
+		AA1EE456176059AB0029C7A5 /* SDL_test_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_crc32.c; sourceTree = "<group>"; };
+		AA1EE457176059AB0029C7A5 /* SDL_test_font.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_font.c; sourceTree = "<group>"; };
+		AA1EE458176059AB0029C7A5 /* SDL_test_fuzzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_fuzzer.c; sourceTree = "<group>"; };
+		AA1EE459176059AB0029C7A5 /* SDL_test_harness.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_harness.c; sourceTree = "<group>"; };
+		AA1EE45A176059AB0029C7A5 /* SDL_test_imageBlit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imageBlit.c; sourceTree = "<group>"; };
+		AA1EE45B176059AB0029C7A5 /* SDL_test_imageBlitBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imageBlitBlend.c; sourceTree = "<group>"; };
+		AA1EE45C176059AB0029C7A5 /* SDL_test_imageFace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imageFace.c; sourceTree = "<group>"; };
+		AA1EE45D176059AB0029C7A5 /* SDL_test_imagePrimitives.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imagePrimitives.c; sourceTree = "<group>"; };
+		AA1EE45E176059AB0029C7A5 /* SDL_test_imagePrimitivesBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imagePrimitivesBlend.c; sourceTree = "<group>"; };
+		AA1EE45F176059AB0029C7A5 /* SDL_test_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_log.c; sourceTree = "<group>"; };
+		AA1EE460176059AB0029C7A5 /* SDL_test_md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_md5.c; sourceTree = "<group>"; };
+		AA1EE461176059AB0029C7A5 /* SDL_test_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_random.c; sourceTree = "<group>"; };
+		AAF02FFF1F9009B100B9A9FB /* SDL_test_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_memory.c; sourceTree = "<group>"; };
+		AAF030001F9009B100B9A9FB /* SDL_test_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_assert.c; sourceTree = "<group>"; };
 		FA3D98F81BC4E5A2002C96C8 /* libSDL2test-TV.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDL2test-TV.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
@@ -128,6 +128,7 @@
 				AA1EE461176059AB0029C7A5 /* SDL_test_random.c */,
 			);
 			name = "Library Source";
+			path = ../../src/test;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
diff --git a/source/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj b/source/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj
old mode 100755
new mode 100644
index 9d71d66..978833d
--- a/source/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj
+++ b/source/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj
@@ -419,52 +419,52 @@
 
 /* Begin PBXFileReference section */
 		046CEF8613254F23007AD51D /* testgesture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testgesture.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		046CEF8913254F63007AD51D /* testgesture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testgesture.c; path = ../../test/testgesture.c; sourceTree = SOURCE_ROOT; };
+		046CEF8913254F63007AD51D /* testgesture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testgesture.c; sourceTree = "<group>"; };
 		047A63ED13285C3200CD7973 /* checkkeys.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = checkkeys.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		047A63F013285CD100CD7973 /* checkkeys.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = checkkeys.c; path = ../../test/checkkeys.c; sourceTree = SOURCE_ROOT; };
+		047A63F013285CD100CD7973 /* checkkeys.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = checkkeys.c; sourceTree = "<group>"; };
 		1D6058910D05DD3D006BFB54 /* testwm2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testwm2.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		56ED050D118A8FE400A56AA6 /* testpower.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testpower.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		56ED0510118A904200A56AA6 /* testpower.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testpower.c; path = ../../test/testpower.c; sourceTree = SOURCE_ROOT; };
+		56ED0510118A904200A56AA6 /* testpower.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testpower.c; sourceTree = "<group>"; };
 		AA13B3261FB8AEBC00D9FEE6 /* testyuv.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testyuv.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		AA13B32E1FB8AF0C00D9FEE6 /* testyuv.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = testyuv.bmp; path = ../../test/testyuv.bmp; sourceTree = "<group>"; };
-		AA13B35B1FB8B4D600D9FEE6 /* testyuv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testyuv.c; path = ../../test/testyuv.c; sourceTree = "<group>"; };
-		AA13B35E1FB8B50D00D9FEE6 /* testyuv_cvt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testyuv_cvt.c; path = ../../test/testyuv_cvt.c; sourceTree = "<group>"; };
+		AA13B32E1FB8AF0C00D9FEE6 /* testyuv.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = testyuv.bmp; sourceTree = "<group>"; };
+		AA13B35B1FB8B4D600D9FEE6 /* testyuv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testyuv.c; sourceTree = "<group>"; };
+		AA13B35E1FB8B50D00D9FEE6 /* testyuv_cvt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testyuv_cvt.c; sourceTree = "<group>"; };
 		AA1EE44D176059220029C7A5 /* SDL2test.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL2test.xcodeproj; path = ../SDLtest/SDL2test.xcodeproj; sourceTree = "<group>"; };
 		AA2F57A91FDB544800832AD7 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
 		AAE7DEEC14CBB1E100DF1A0E /* testscale.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testscale.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		AAE7DF4514CBB43900DF1A0E /* testscale.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testscale.c; path = ../../test/testscale.c; sourceTree = "<group>"; };
+		AAE7DF4514CBB43900DF1A0E /* testscale.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testscale.c; sourceTree = "<group>"; };
 		AAE7DFB114CBB54E00DF1A0E /* testrendertarget.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testrendertarget.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		AAE7DFB414CBB5F700DF1A0E /* testrendertarget.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testrendertarget.c; path = ../../test/testrendertarget.c; sourceTree = "<group>"; };
-		FA0EF2221BAF43DE000E07A6 /* testgamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testgamecontroller.c; path = ../../test/testgamecontroller.c; sourceTree = "<group>"; };
-		FA0EF2281BAF4487000E07A6 /* axis.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = axis.bmp; path = ../../test/axis.bmp; sourceTree = "<group>"; };
-		FA0EF2291BAF4487000E07A6 /* button.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = button.bmp; path = ../../test/button.bmp; sourceTree = "<group>"; };
-		FA0EF22A1BAF4487000E07A6 /* controllermap.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = controllermap.bmp; path = ../../test/controllermap.bmp; sourceTree = "<group>"; };
+		AAE7DFB414CBB5F700DF1A0E /* testrendertarget.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testrendertarget.c; sourceTree = "<group>"; };
+		FA0EF2221BAF43DE000E07A6 /* testgamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testgamecontroller.c; sourceTree = "<group>"; };
+		FA0EF2281BAF4487000E07A6 /* axis.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = axis.bmp; sourceTree = "<group>"; };
+		FA0EF2291BAF4487000E07A6 /* button.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = button.bmp; sourceTree = "<group>"; };
+		FA0EF22A1BAF4487000E07A6 /* controllermap.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = controllermap.bmp; sourceTree = "<group>"; };
 		FA3D99341BC4E644002C96C8 /* testgamecontroller-TV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "testgamecontroller-TV.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		FA684F7A1BAF1A4400DCFD1A /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
 		FA8B4BAC1967076F00F8EB7C /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; };
 		FABA34761D8B4EAD00915323 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
 		FABA34911D8B575200915323 /* testaudiocapture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testaudiocapture.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		FABA34931D8B578200915323 /* testaudiocapture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testaudiocapture.c; path = ../../test/testaudiocapture.c; sourceTree = "<group>"; };
+		FABA34931D8B578200915323 /* testaudiocapture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testaudiocapture.c; sourceTree = "<group>"; };
 		FABA34AA1D8B582100915323 /* loopwav-TV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "loopwav-TV.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		FAE0E9931BAF9B230098DFA4 /* testgamecontroller.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testgamecontroller.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FD1B48AC0E3131CA007AB34E /* SDL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL.xcodeproj; path = ../SDL/SDL.xcodeproj; sourceTree = SOURCE_ROOT; };
-		FDA8A7410E2D0F1600EA573E /* testaudioinfo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testaudioinfo.c; path = ../../test/testaudioinfo.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7470E2D0F1600EA573E /* testerror.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testerror.c; path = ../../test/testerror.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7480E2D0F1600EA573E /* testfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testfile.c; path = ../../test/testfile.c; sourceTree = SOURCE_ROOT; };
-		FDA8A74C0E2D0F1600EA573E /* testthread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testthread.c; path = ../../test/testthread.c; sourceTree = SOURCE_ROOT; };
-		FDA8A74D0E2D0F1600EA573E /* testiconv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testiconv.c; path = ../../test/testiconv.c; sourceTree = SOURCE_ROOT; };
-		FDA8A74E0E2D0F1600EA573E /* testjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testjoystick.c; path = ../../test/testjoystick.c; sourceTree = SOURCE_ROOT; };
-		FDA8A74F0E2D0F1600EA573E /* testkeys.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testkeys.c; path = ../../test/testkeys.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7510E2D0F1600EA573E /* testlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testlock.c; path = ../../test/testlock.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7540E2D0F1600EA573E /* testoverlay2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testoverlay2.c; path = ../../test/testoverlay2.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7560E2D0F1600EA573E /* testplatform.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testplatform.c; path = ../../test/testplatform.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7570E2D0F1600EA573E /* testsem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testsem.c; path = ../../test/testsem.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7590E2D0F1600EA573E /* testsprite2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testsprite2.c; path = ../../test/testsprite2.c; sourceTree = SOURCE_ROOT; };
-		FDA8A75A0E2D0F1600EA573E /* testtimer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testtimer.c; path = ../../test/testtimer.c; sourceTree = SOURCE_ROOT; };
-		FDA8A75B0E2D0F1600EA573E /* testver.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testver.c; path = ../../test/testver.c; sourceTree = SOURCE_ROOT; };
-		FDA8A75F0E2D0F1600EA573E /* testwm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testwm2.c; path = ../../test/testwm2.c; sourceTree = SOURCE_ROOT; };
-		FDA8A7610E2D0F1600EA573E /* torturethread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = torturethread.c; path = ../../test/torturethread.c; sourceTree = SOURCE_ROOT; };
-		FDA8A78B0E2D0F3D00EA573E /* loopwave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = loopwave.c; path = ../../test/loopwave.c; sourceTree = SOURCE_ROOT; };
+		FDA8A7410E2D0F1600EA573E /* testaudioinfo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testaudioinfo.c; sourceTree = "<group>"; };
+		FDA8A7470E2D0F1600EA573E /* testerror.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testerror.c; sourceTree = "<group>"; };
+		FDA8A7480E2D0F1600EA573E /* testfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testfile.c; sourceTree = "<group>"; };
+		FDA8A74C0E2D0F1600EA573E /* testthread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testthread.c; sourceTree = "<group>"; };
+		FDA8A74D0E2D0F1600EA573E /* testiconv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testiconv.c; sourceTree = "<group>"; };
+		FDA8A74E0E2D0F1600EA573E /* testjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testjoystick.c; sourceTree = "<group>"; };
+		FDA8A74F0E2D0F1600EA573E /* testkeys.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testkeys.c; sourceTree = "<group>"; };
+		FDA8A7510E2D0F1600EA573E /* testlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testlock.c; sourceTree = "<group>"; };
+		FDA8A7540E2D0F1600EA573E /* testoverlay2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testoverlay2.c; sourceTree = "<group>"; };
+		FDA8A7560E2D0F1600EA573E /* testplatform.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testplatform.c; sourceTree = "<group>"; };
+		FDA8A7570E2D0F1600EA573E /* testsem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testsem.c; sourceTree = "<group>"; };
+		FDA8A7590E2D0F1600EA573E /* testsprite2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testsprite2.c; sourceTree = "<group>"; };
+		FDA8A75A0E2D0F1600EA573E /* testtimer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testtimer.c; sourceTree = "<group>"; };
+		FDA8A75B0E2D0F1600EA573E /* testver.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testver.c; sourceTree = "<group>"; };
+		FDA8A75F0E2D0F1600EA573E /* testwm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testwm2.c; sourceTree = "<group>"; };
+		FDA8A7610E2D0F1600EA573E /* torturethread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = torturethread.c; sourceTree = "<group>"; };
+		FDA8A78B0E2D0F3D00EA573E /* loopwave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loopwave.c; sourceTree = "<group>"; };
 		FDA8A8980E2D111A00EA573E /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
 		FDA8A8990E2D111A00EA573E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		FDA8A89A0E2D111A00EA573E /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
@@ -473,21 +473,21 @@
 		FDA8A89D0E2D111A00EA573E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
 		FDA8A89E0E2D111A00EA573E /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
 		FDA8AABB0E2D330F00EA573E /* loopwav.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = loopwav.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		FDA8AAD90E2D33B000EA573E /* icon.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = icon.bmp; path = ../../test/icon.bmp; sourceTree = SOURCE_ROOT; };
-		FDA8AADA0E2D33BA00EA573E /* moose.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = moose.dat; path = ../../test/moose.dat; sourceTree = SOURCE_ROOT; };
-		FDA8AADB0E2D33BA00EA573E /* picture.xbm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = picture.xbm; path = ../../test/picture.xbm; sourceTree = SOURCE_ROOT; };
-		FDA8AADE0E2D33C100EA573E /* sample.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = sample.bmp; path = ../../test/sample.bmp; sourceTree = SOURCE_ROOT; };
-		FDA8AAE20E2D33C600EA573E /* sample.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sample.wav; path = ../../test/sample.wav; sourceTree = SOURCE_ROOT; };
+		FDA8AAD90E2D33B000EA573E /* icon.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = icon.bmp; sourceTree = "<group>"; };
+		FDA8AADA0E2D33BA00EA573E /* moose.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = moose.dat; sourceTree = "<group>"; };
+		FDA8AADB0E2D33BA00EA573E /* picture.xbm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = picture.xbm; sourceTree = "<group>"; };
+		FDA8AADE0E2D33C100EA573E /* sample.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = sample.bmp; sourceTree = "<group>"; };
+		FDA8AAE20E2D33C600EA573E /* sample.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = sample.wav; sourceTree = "<group>"; };
 		FDAAC3CD0E2D47E6001DB1D8 /* testaudioinfo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testaudioinfo.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FDAAC59B0E2D5429001DB1D8 /* testerror.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testerror.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FDAAC5C90E2D55B5001DB1D8 /* testfile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testfile.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FDAAC6260E2D5914001DB1D8 /* testgles.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testgles.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		FDAAC6290E2D5960001DB1D8 /* testgles.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testgles.c; path = ../../test/testgles.c; sourceTree = SOURCE_ROOT; };
+		FDAAC6290E2D5960001DB1D8 /* testgles.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testgles.c; sourceTree = "<group>"; };
 		FDC430000F0D866D009C87E1 /* torturethread.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = torturethread.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		FDC430090F0D86BF009C87E1 /* testdraw2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testdraw2.c; path = ../../test/testdraw2.c; sourceTree = SOURCE_ROOT; };
+		FDC430090F0D86BF009C87E1 /* testdraw2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testdraw2.c; sourceTree = "<group>"; };
 		FDD2C10A0E2E4F4B00B7A85F /* testthread.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testthread.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FDD2C1810E2E52C000B7A85F /* testiconv.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testiconv.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		FDD2C18A0E2E52FE00B7A85F /* utf8.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = utf8.txt; path = ../../test/utf8.txt; sourceTree = SOURCE_ROOT; };
+		FDD2C18A0E2E52FE00B7A85F /* utf8.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = utf8.txt; sourceTree = "<group>"; };
 		FDD2C1A50E2E534F00B7A85F /* testjoystick.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testjoystick.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FDD2C45E0E2E773800B7A85F /* testkeys.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testkeys.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		FDD2C47C0E2E77D700B7A85F /* testlock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testlock.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1057,7 +1057,7 @@
 				AA1EE44D176059220029C7A5 /* SDL2test.xcodeproj */,
 				FD1B48AC0E3131CA007AB34E /* SDL.xcodeproj */,
 				FDA8AAD60E2D339A00EA573E /* Resources */,
-				FDA8A7C30E2D10FA00EA573E /* Linked Frameworks */,
+				FDA8A7C30E2D10FA00EA573E /* Frameworks */,
 				FDA8A73B0E2D0F0400EA573E /* src */,
 				19C28FACFE9D520D11CA2CBB /* Products */,
 				FABA34751D8B4EAC00915323 /* Frameworks */,
@@ -1125,9 +1125,10 @@
 				FDA8A7610E2D0F1600EA573E /* torturethread.c */,
 			);
 			name = src;
+			path = ../../test;
 			sourceTree = "<group>";
 		};
-		FDA8A7C30E2D10FA00EA573E /* Linked Frameworks */ = {
+		FDA8A7C30E2D10FA00EA573E /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
 				FA684F7A1BAF1A4400DCFD1A /* GameController.framework */,
@@ -1140,7 +1141,7 @@
 				FDA8A89D0E2D111A00EA573E /* Foundation.framework */,
 				FDA8A89E0E2D111A00EA573E /* CoreAudio.framework */,
 			);
-			name = "Linked Frameworks";
+			name = Frameworks;
 			sourceTree = "<group>";
 		};
 		FDA8AAD60E2D339A00EA573E /* Resources */ = {
@@ -1158,6 +1159,7 @@
 				FDD2C18A0E2E52FE00B7A85F /* utf8.txt */,
 			);
 			name = Resources;
+			path = ../../test;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
@@ -2324,6 +2326,7 @@
 		C01FCF4F08A954540054247B /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				ENABLE_BITCODE = NO;
 				GCC_OPTIMIZATION_LEVEL = 0;
@@ -2340,6 +2343,7 @@
 		C01FCF5008A954540054247B /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				ENABLE_BITCODE = NO;
 				HEADER_SEARCH_PATHS = ../../include;
diff --git a/source/Xcode/SDL/Info-Framework.plist b/source/Xcode/SDL/Info-Framework.plist
index ff3c220..0b89cc4 100644
--- a/source/Xcode/SDL/Info-Framework.plist
+++ b/source/Xcode/SDL/Info-Framework.plist
@@ -19,10 +19,10 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>2.0.8</string>
+	<string>2.0.9</string>
 	<key>CFBundleSignature</key>
 	<string>SDLX</string>
 	<key>CFBundleVersion</key>
-	<string>2.0.8</string>
+	<string>2.0.9</string>
 </dict>
 </plist>
diff --git a/source/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/source/Xcode/SDL/SDL.xcodeproj/project.pbxproj
old mode 100755
new mode 100644
index a39186b..d6041be
--- a/source/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/source/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -397,12 +397,6 @@
 		5646243A1FF821FF0074AC87 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564624371FF821CB0074AC87 /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
 		5646243B1FF822100074AC87 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564624351FF821B80074AC87 /* QuartzCore.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
 		5646243C1FF822170074AC87 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564624371FF821CB0074AC87 /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		565AF96A1FF8238D0077498A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 565AF9691FF823840077498A /* QuartzCore.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		565AF96B1FF8238E0077498A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 565AF9691FF823840077498A /* QuartzCore.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		565AF96C1FF8238E0077498A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 565AF9691FF823840077498A /* QuartzCore.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		565AF96D1FF823980077498A /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 565AF9681FF8237A0077498A /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		565AF96E1FF823980077498A /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 565AF9681FF8237A0077498A /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		565AF96F1FF823990077498A /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 565AF9681FF8237A0077498A /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
 		566CDE8F148F0AC200C5A9BB /* SDL_dropevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 566CDE8D148F0AC200C5A9BB /* SDL_dropevents_c.h */; };
 		566CDE90148F0AC200C5A9BB /* SDL_dropevents.c in Sources */ = {isa = PBXBuildFile; fileRef = 566CDE8E148F0AC200C5A9BB /* SDL_dropevents.c */; };
 		567E2F1C17C44BB2005F1892 /* SDL_sysfilesystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 567E2F1B17C44BB2005F1892 /* SDL_sysfilesystem.m */; };
@@ -462,6 +456,30 @@
 		5C2EF6FE1FC9EE65003F5197 /* SDL_egl.c in Sources */ = {isa = PBXBuildFile; fileRef = 5C2EF6F51FC9EE35003F5197 /* SDL_egl.c */; };
 		5C2EF6FF1FC9EE65003F5197 /* SDL_rect_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2EF6F41FC9EE34003F5197 /* SDL_rect_c.h */; };
 		5C2EF7011FC9EF10003F5197 /* SDL_egl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */; };
+		A704170920F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
+		A704170A20F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
+		A704170B20F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
+		A704171420F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
+		A704171520F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
+		A704171620F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
+		A704171720F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
+		A704171820F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
+		A704171920F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
+		A704171A20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
+		A704171B20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
+		A704171C20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
+		A704171D20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
+		A704171E20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
+		A704171F20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
+		A704172020F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
+		A704172120F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
+		A704172220F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
+		A704172320F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
+		A704172420F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
+		A704172520F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
+		A704172620F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
+		A704172720F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
+		A704172820F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
 		A7381E961D8B69D600B177DD /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7381E951D8B69D600B177DD /* CoreAudio.framework */; };
 		A7381E971D8B6A0300B177DD /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7381E931D8B69C300B177DD /* AudioToolbox.framework */; };
 		A77E6EB4167AB0A90010E40B /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -880,6 +898,30 @@
 		DB31407217554B71006C0E22 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 007317C10858E15000B2BC32 /* Carbon.framework */; };
 		DB31408B17554D37006C0E22 /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CFA89C106B4BA100758660 /* ForceFeedback.framework */; };
 		DB31408D17554D3C006C0E22 /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CFA89C106B4BA100758660 /* ForceFeedback.framework */; };
+		F30D9C84212BC94F0047DF2E /* SDL_sensor_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C81212BC94E0047DF2E /* SDL_sensor_c.h */; };
+		F30D9C85212BC94F0047DF2E /* SDL_sensor_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C81212BC94E0047DF2E /* SDL_sensor_c.h */; };
+		F30D9C86212BC94F0047DF2E /* SDL_sensor_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C81212BC94E0047DF2E /* SDL_sensor_c.h */; };
+		F30D9C87212BC94F0047DF2E /* SDL_syssensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C82212BC94F0047DF2E /* SDL_syssensor.h */; };
+		F30D9C88212BC94F0047DF2E /* SDL_syssensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C82212BC94F0047DF2E /* SDL_syssensor.h */; };
+		F30D9C89212BC94F0047DF2E /* SDL_syssensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C82212BC94F0047DF2E /* SDL_syssensor.h */; };
+		F30D9C8A212BC94F0047DF2E /* SDL_sensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C83212BC94F0047DF2E /* SDL_sensor.c */; };
+		F30D9C8B212BC94F0047DF2E /* SDL_sensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C83212BC94F0047DF2E /* SDL_sensor.c */; };
+		F30D9C8C212BC94F0047DF2E /* SDL_sensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C83212BC94F0047DF2E /* SDL_sensor.c */; };
+		F30D9C90212CABDC0047DF2E /* SDL_dummysensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C8E212CABDB0047DF2E /* SDL_dummysensor.h */; };
+		F30D9C91212CABDC0047DF2E /* SDL_dummysensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C8E212CABDB0047DF2E /* SDL_dummysensor.h */; };
+		F30D9C92212CABDC0047DF2E /* SDL_dummysensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9C8E212CABDB0047DF2E /* SDL_dummysensor.h */; };
+		F30D9C93212CABDC0047DF2E /* SDL_dummysensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C8F212CABDB0047DF2E /* SDL_dummysensor.c */; };
+		F30D9C94212CABDC0047DF2E /* SDL_dummysensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C8F212CABDB0047DF2E /* SDL_dummysensor.c */; };
+		F30D9C95212CABDC0047DF2E /* SDL_dummysensor.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9C8F212CABDB0047DF2E /* SDL_dummysensor.c */; };
+		F30D9CCD212EB4810047DF2E /* SDL_displayevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9CCB212EB4810047DF2E /* SDL_displayevents_c.h */; };
+		F30D9CCE212EB4810047DF2E /* SDL_displayevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9CCB212EB4810047DF2E /* SDL_displayevents_c.h */; };
+		F30D9CCF212EB4810047DF2E /* SDL_displayevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9CCB212EB4810047DF2E /* SDL_displayevents_c.h */; };
+		F30D9CD0212EB4810047DF2E /* SDL_displayevents.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CCC212EB4810047DF2E /* SDL_displayevents.c */; };
+		F30D9CD1212EB4810047DF2E /* SDL_displayevents.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CCC212EB4810047DF2E /* SDL_displayevents.c */; };
+		F30D9CD2212EB4810047DF2E /* SDL_displayevents.c in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CCC212EB4810047DF2E /* SDL_displayevents.c */; };
+		F3950CD8212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		F3950CD9212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		F3950CDA212BC88D00F51292 /* SDL_sensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3950CD7212BC88D00F51292 /* SDL_sensor.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; };
 		FA73671E19A54140004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; };
 		FA73671F19A54144004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; };
@@ -897,12 +939,12 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
-		0073179D0858DECD00B2BC32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
-		0073179F0858DECD00B2BC32 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
-		007317C10858E15000B2BC32 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+		0073179D0858DECD00B2BC32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+		0073179F0858DECD00B2BC32 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
+		007317C10858E15000B2BC32 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
 		00794D3F09D0C461003FC8A1 /* License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
-		00CFA89C106B4BA100758660 /* ForceFeedback.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ForceFeedback.framework; path = /System/Library/Frameworks/ForceFeedback.framework; sourceTree = "<absolute>"; };
-		00D0D08310675DD9004B05EF /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
+		00CFA89C106B4BA100758660 /* ForceFeedback.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ForceFeedback.framework; path = System/Library/Frameworks/ForceFeedback.framework; sourceTree = SDKROOT; };
+		00D0D08310675DD9004B05EF /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
 		04043BBA12FEB1BE0076DB1F /* SDL_glfuncs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_glfuncs.h; sourceTree = "<group>"; };
 		041B2C9E12FA0D680087D585 /* SDL_render.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render.c; sourceTree = "<group>"; };
 		041B2C9F12FA0D680087D585 /* SDL_sysrender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysrender.h; sourceTree = "<group>"; };
@@ -915,8 +957,8 @@
 		0442EC1B12FE1BCB004C9285 /* SDL_render_sw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_sw.c; sourceTree = "<group>"; };
 		0442EC5812FE1C60004C9285 /* SDL_x11framebuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_x11framebuffer.c; sourceTree = "<group>"; };
 		0442EC5912FE1C60004C9285 /* SDL_x11framebuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_x11framebuffer.h; sourceTree = "<group>"; };
-		0442EC5E12FE1C75004C9285 /* SDL_hints.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_hints.c; path = ../../src/SDL_hints.c; sourceTree = SOURCE_ROOT; };
-		04BAC0C71300C2160055DE28 /* SDL_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_log.c; path = ../../src/SDL_log.c; sourceTree = SOURCE_ROOT; };
+		0442EC5E12FE1C75004C9285 /* SDL_hints.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hints.c; sourceTree = "<group>"; };
+		04BAC0C71300C2160055DE28 /* SDL_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_log.c; sourceTree = "<group>"; };
 		04BDFD7412E6671700899322 /* SDL_atomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_atomic.c; sourceTree = "<group>"; };
 		04BDFD7512E6671700899322 /* SDL_spinlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_spinlock.c; sourceTree = "<group>"; };
 		04BDFD8812E6671700899322 /* SDL_diskaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_diskaudio.c; sourceTree = "<group>"; };
@@ -971,11 +1013,11 @@
 		04BDFE3312E6671700899322 /* SDL_sysloadso.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysloadso.c; sourceTree = "<group>"; };
 		04BDFE4B12E6671700899322 /* SDL_syspower.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_syspower.c; sourceTree = "<group>"; };
 		04BDFE4E12E6671700899322 /* SDL_power.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_power.c; sourceTree = "<group>"; };
-		04BDFE5512E6671700899322 /* SDL_assert_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_assert_c.h; path = ../../src/SDL_assert_c.h; sourceTree = SOURCE_ROOT; };
-		04BDFE5612E6671700899322 /* SDL_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_assert.c; path = ../../src/SDL_assert.c; sourceTree = SOURCE_ROOT; };
-		04BDFE5812E6671700899322 /* SDL_error_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_error_c.h; path = ../../src/SDL_error_c.h; sourceTree = SOURCE_ROOT; };
-		04BDFE5912E6671700899322 /* SDL_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_error.c; path = ../../src/SDL_error.c; sourceTree = SOURCE_ROOT; };
-		04BDFE5C12E6671700899322 /* SDL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL.c; path = ../../src/SDL.c; sourceTree = SOURCE_ROOT; };
+		04BDFE5512E6671700899322 /* SDL_assert_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_assert_c.h; sourceTree = "<group>"; };
+		04BDFE5612E6671700899322 /* SDL_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_assert.c; sourceTree = "<group>"; };
+		04BDFE5812E6671700899322 /* SDL_error_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_error_c.h; sourceTree = "<group>"; };
+		04BDFE5912E6671700899322 /* SDL_error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_error.c; sourceTree = "<group>"; };
+		04BDFE5C12E6671700899322 /* SDL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL.c; sourceTree = "<group>"; };
 		04BDFE5E12E6671700899322 /* SDL_getenv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_getenv.c; sourceTree = "<group>"; };
 		04BDFE5F12E6671700899322 /* SDL_iconv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_iconv.c; sourceTree = "<group>"; };
 		04BDFE6012E6671700899322 /* SDL_malloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_malloc.c; sourceTree = "<group>"; };
@@ -1089,21 +1131,19 @@
 		4D1664521EDD60AD003DE88E /* SDL_cocoavulkan.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_cocoavulkan.m; sourceTree = "<group>"; };
 		4D4820431F0F10B400EDC31C /* SDL_vulkan.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_vulkan.h; sourceTree = "<group>"; };
 		4D7517281EE2562B00820EEA /* SDL_cocoametalview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_cocoametalview.h; sourceTree = "<group>"; };
-		56115BB91DF72C6D00F47E1E /* SDL_dataqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_dataqueue.c; path = ../../src/SDL_dataqueue.c; sourceTree = "<group>"; };
-		56115BBA1DF72C6D00F47E1E /* SDL_dataqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dataqueue.h; path = ../../src/SDL_dataqueue.h; sourceTree = "<group>"; };
+		56115BB91DF72C6D00F47E1E /* SDL_dataqueue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dataqueue.c; sourceTree = "<group>"; };
+		56115BBA1DF72C6D00F47E1E /* SDL_dataqueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dataqueue.h; sourceTree = "<group>"; };
 		564624351FF821B80074AC87 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		564624371FF821CB0074AC87 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
-		565AF9681FF8237A0077498A /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Metal.framework; sourceTree = "<group>"; };
-		565AF9691FF823840077498A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
 		566CDE8D148F0AC200C5A9BB /* SDL_dropevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dropevents_c.h; sourceTree = "<group>"; };
 		566CDE8E148F0AC200C5A9BB /* SDL_dropevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dropevents.c; sourceTree = "<group>"; };
-		567E2F1B17C44BB2005F1892 /* SDL_sysfilesystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_sysfilesystem.m; path = ../../src/filesystem/cocoa/SDL_sysfilesystem.m; sourceTree = "<group>"; };
+		567E2F1B17C44BB2005F1892 /* SDL_sysfilesystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_sysfilesystem.m; path = cocoa/SDL_sysfilesystem.m; sourceTree = "<group>"; };
 		567E2F2017C44C35005F1892 /* SDL_filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_filesystem.h; sourceTree = "<group>"; };
-		56A670081856545C0007D20F /* SDL_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_internal.h; path = ../../src/SDL_internal.h; sourceTree = "<group>"; };
-		56A6701D185654B40007D20F /* SDL_dynapi_procs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dynapi_procs.h; path = ../../src/dynapi/SDL_dynapi_procs.h; sourceTree = "<group>"; };
-		56A6701E185654B40007D20F /* SDL_dynapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_dynapi.c; path = ../../src/dynapi/SDL_dynapi.c; sourceTree = "<group>"; };
-		56A6701F185654B40007D20F /* SDL_dynapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dynapi.h; path = ../../src/dynapi/SDL_dynapi.h; sourceTree = "<group>"; };
-		56A67020185654B40007D20F /* SDL_dynapi_overrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_dynapi_overrides.h; path = ../../src/dynapi/SDL_dynapi_overrides.h; sourceTree = "<group>"; };
+		56A670081856545C0007D20F /* SDL_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_internal.h; sourceTree = "<group>"; };
+		56A6701D185654B40007D20F /* SDL_dynapi_procs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi_procs.h; sourceTree = "<group>"; };
+		56A6701E185654B40007D20F /* SDL_dynapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dynapi.c; sourceTree = "<group>"; };
+		56A6701F185654B40007D20F /* SDL_dynapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi.h; sourceTree = "<group>"; };
+		56A67020185654B40007D20F /* SDL_dynapi_overrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dynapi_overrides.h; sourceTree = "<group>"; };
 		5C2EF69B1FC987C6003F5197 /* SDL_gles2funcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gles2funcs.h; sourceTree = "<group>"; };
 		5C2EF69C1FC987C6003F5197 /* SDL_render_gles2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_gles2.c; sourceTree = "<group>"; };
 		5C2EF69D1FC987C6003F5197 /* SDL_shaders_gles2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_gles2.h; sourceTree = "<group>"; };
@@ -1114,6 +1154,14 @@
 		5C2EF6F51FC9EE35003F5197 /* SDL_egl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_egl.c; sourceTree = "<group>"; };
 		5C2EF6F61FC9EE35003F5197 /* SDL_egl_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl_c.h; sourceTree = "<group>"; };
 		5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl.h; sourceTree = "<group>"; };
+		A704170820F09A9800A82227 /* hid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hid.c; sourceTree = "<group>"; };
+		A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapijoystick.c; sourceTree = "<group>"; };
+		A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapijoystick_c.h; sourceTree = "<group>"; };
+		A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = "<group>"; };
+		A704171020F09AC900A82227 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = "<group>"; };
+		A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps4.c; sourceTree = "<group>"; };
+		A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = "<group>"; };
+		A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = "<group>"; };
 		A7381E931D8B69C300B177DD /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
 		A7381E951D8B69D600B177DD /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
 		A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = "<group>"; };
@@ -1197,10 +1245,18 @@
 		D55A1B80179F262300625D7C /* SDL_cocoamousetap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_cocoamousetap.m; sourceTree = "<group>"; };
 		DB31407717554B71006C0E22 /* libSDL2.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libSDL2.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB89958518A1A5C50092407C /* SDL_syshaptic_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_syshaptic_c.h; sourceTree = "<group>"; };
+		F30D9C81212BC94E0047DF2E /* SDL_sensor_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sensor_c.h; sourceTree = "<group>"; };
+		F30D9C82212BC94F0047DF2E /* SDL_syssensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syssensor.h; sourceTree = "<group>"; };
+		F30D9C83212BC94F0047DF2E /* SDL_sensor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sensor.c; sourceTree = "<group>"; };
+		F30D9C8E212CABDB0047DF2E /* SDL_dummysensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dummysensor.h; sourceTree = "<group>"; };
+		F30D9C8F212CABDB0047DF2E /* SDL_dummysensor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dummysensor.c; sourceTree = "<group>"; };
+		F30D9CCB212EB4810047DF2E /* SDL_displayevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_displayevents_c.h; sourceTree = "<group>"; };
+		F30D9CCC212EB4810047DF2E /* SDL_displayevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_displayevents.c; sourceTree = "<group>"; };
+		F3950CD7212BC88D00F51292 /* SDL_sensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sensor.h; sourceTree = "<group>"; };
 		F59C710300D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
 		F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
 		F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
-		FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = /System/Library/Frameworks/CoreVideo.framework; sourceTree = "<absolute>"; };
+		FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
 		FABA34C61D8B5DB100915323 /* SDL_coreaudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_coreaudio.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -1212,14 +1268,12 @@
 				564624381FF821DA0074AC87 /* Metal.framework in Frameworks */,
 				564624361FF821C20074AC87 /* QuartzCore.framework in Frameworks */,
 				A7381E971D8B6A0300B177DD /* AudioToolbox.framework in Frameworks */,
-				565AF96A1FF8238D0077498A /* QuartzCore.framework in Frameworks */,
 				00D0D0D810675E46004B05EF /* Carbon.framework in Frameworks */,
 				007317A40858DECD00B2BC32 /* Cocoa.framework in Frameworks */,
 				A7381E961D8B69D600B177DD /* CoreAudio.framework in Frameworks */,
 				00D0D08410675DD9004B05EF /* CoreFoundation.framework in Frameworks */,
 				FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */,
 				00CFA89D106B4BA100758660 /* ForceFeedback.framework in Frameworks */,
-				565AF96D1FF823980077498A /* Metal.framework in Frameworks */,
 				007317A60858DECD00B2BC32 /* IOKit.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1231,14 +1285,12 @@
 				5646243A1FF821FF0074AC87 /* Metal.framework in Frameworks */,
 				564624391FF821EF0074AC87 /* QuartzCore.framework in Frameworks */,
 				56C5237E1D8F4985001F2F30 /* CoreAudio.framework in Frameworks */,
-				565AF96B1FF8238E0077498A /* QuartzCore.framework in Frameworks */,
 				FA73671E19A54140004122E4 /* CoreVideo.framework in Frameworks */,
 				007317AB0858DECD00B2BC32 /* Cocoa.framework in Frameworks */,
 				007317AD0858DECD00B2BC32 /* IOKit.framework in Frameworks */,
 				56C523801D8F498B001F2F30 /* CoreFoundation.framework in Frameworks */,
 				007317C30858E15000B2BC32 /* Carbon.framework in Frameworks */,
 				DB31408B17554D37006C0E22 /* ForceFeedback.framework in Frameworks */,
-				565AF96E1FF823980077498A /* Metal.framework in Frameworks */,
 				562C4AE91D8F496200AF9EBE /* AudioToolbox.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1250,14 +1302,12 @@
 				5646243C1FF822170074AC87 /* Metal.framework in Frameworks */,
 				5646243B1FF822100074AC87 /* QuartzCore.framework in Frameworks */,
 				56C5237F1D8F4985001F2F30 /* CoreAudio.framework in Frameworks */,
-				565AF96C1FF8238E0077498A /* QuartzCore.framework in Frameworks */,
 				FA73671F19A54144004122E4 /* CoreVideo.framework in Frameworks */,
 				DB31406E17554B71006C0E22 /* Cocoa.framework in Frameworks */,
 				DB31407017554B71006C0E22 /* IOKit.framework in Frameworks */,
 				56C523811D8F498C001F2F30 /* CoreFoundation.framework in Frameworks */,
 				DB31407217554B71006C0E22 /* Carbon.framework in Frameworks */,
 				DB31408D17554D3C006C0E22 /* ForceFeedback.framework in Frameworks */,
-				565AF96F1FF823990077498A /* Metal.framework in Frameworks */,
 				562C4AEA1D8F496300AF9EBE /* AudioToolbox.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1316,6 +1366,7 @@
 				AA7557EB1595D4D800BBD41B /* SDL_revision.h */,
 				AA7557EC1595D4D800BBD41B /* SDL_rwops.h */,
 				AA7557ED1595D4D800BBD41B /* SDL_scancode.h */,
+				F3950CD7212BC88D00F51292 /* SDL_sensor.h */,
 				AA7557EE1595D4D800BBD41B /* SDL_shape.h */,
 				AA7557EF1595D4D800BBD41B /* SDL_stdinc.h */,
 				AA7557F01595D4D800BBD41B /* SDL_surface.h */,
@@ -1337,7 +1388,6 @@
 		034768DDFF38A45A11DB9C8B /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				089C1665FE841158C02AAC07 /* Resources */,
 				BECDF66C0761BA81005FE872 /* SDL2.framework */,
 				BECDF6B30761BA81005FE872 /* libSDL2.a */,
 				BECDF6BE0761BA81005FE872 /* Standard DMG */,
@@ -1358,9 +1408,8 @@
 				04409B8F12FA97ED00FB9AA8 /* SDL_yuv_sw_c.h */,
 				04409B9012FA97ED00FB9AA8 /* SDL_yuv_sw.c */,
 			);
-			name = render;
-			path = ../../src/render;
-			sourceTree = SOURCE_ROOT;
+			path = render;
+			sourceTree = "<group>";
 		};
 		041B2C9A12FA0D680087D585 /* opengl */ = {
 			isa = PBXGroup;
@@ -1401,9 +1450,8 @@
 				04BDFD7412E6671700899322 /* SDL_atomic.c */,
 				04BDFD7512E6671700899322 /* SDL_spinlock.c */,
 			);
-			name = atomic;
-			path = ../../src/atomic;
-			sourceTree = SOURCE_ROOT;
+			path = atomic;
+			sourceTree = "<group>";
 		};
 		04BDFD7612E6671700899322 /* audio */ = {
 			isa = PBXGroup;
@@ -1422,9 +1470,8 @@
 				04BDFDC312E6671700899322 /* SDL_wave.c */,
 				04BDFDC412E6671700899322 /* SDL_wave.h */,
 			);
-			name = audio;
-			path = ../../src/audio;
-			sourceTree = SOURCE_ROOT;
+			path = audio;
+			sourceTree = "<group>";
 		};
 		04BDFD8712E6671700899322 /* disk */ = {
 			isa = PBXGroup;
@@ -1458,9 +1505,8 @@
 			children = (
 				04BDFDD412E6671700899322 /* SDL_cpuinfo.c */,
 			);
-			name = cpuinfo;
-			path = ../../src/cpuinfo;
-			sourceTree = SOURCE_ROOT;
+			path = cpuinfo;
+			sourceTree = "<group>";
 		};
 		04BDFDD512E6671700899322 /* events */ = {
 			isa = PBXGroup;
@@ -1470,28 +1516,29 @@
 				04BDFDD812E6671700899322 /* scancodes_darwin.h */,
 				04BDFDD912E6671700899322 /* scancodes_linux.h */,
 				04BDFDDB12E6671700899322 /* scancodes_xfree86.h */,
-				04BDFDDC12E6671700899322 /* SDL_clipboardevents.c */,
 				04BDFDDD12E6671700899322 /* SDL_clipboardevents_c.h */,
+				04BDFDDC12E6671700899322 /* SDL_clipboardevents.c */,
+				F30D9CCB212EB4810047DF2E /* SDL_displayevents_c.h */,
+				F30D9CCC212EB4810047DF2E /* SDL_displayevents.c */,
 				566CDE8D148F0AC200C5A9BB /* SDL_dropevents_c.h */,
 				566CDE8E148F0AC200C5A9BB /* SDL_dropevents.c */,
-				04BDFDDE12E6671700899322 /* SDL_events.c */,
 				04BDFDDF12E6671700899322 /* SDL_events_c.h */,
-				04BDFDE012E6671700899322 /* SDL_gesture.c */,
+				04BDFDDE12E6671700899322 /* SDL_events.c */,
 				04BDFDE112E6671700899322 /* SDL_gesture_c.h */,
-				04BDFDE212E6671700899322 /* SDL_keyboard.c */,
+				04BDFDE012E6671700899322 /* SDL_gesture.c */,
 				04BDFDE312E6671700899322 /* SDL_keyboard_c.h */,
-				04BDFDE412E6671700899322 /* SDL_mouse.c */,
+				04BDFDE212E6671700899322 /* SDL_keyboard.c */,
 				04BDFDE512E6671700899322 /* SDL_mouse_c.h */,
+				04BDFDE412E6671700899322 /* SDL_mouse.c */,
 				04BDFDE612E6671700899322 /* SDL_quit.c */,
 				04BDFDE712E6671700899322 /* SDL_sysevents.h */,
-				04BDFDE812E6671700899322 /* SDL_touch.c */,
 				04BDFDE912E6671700899322 /* SDL_touch_c.h */,
-				04BDFDEA12E6671700899322 /* SDL_windowevents.c */,
+				04BDFDE812E6671700899322 /* SDL_touch.c */,
 				04BDFDEB12E6671700899322 /* SDL_windowevents_c.h */,
+				04BDFDEA12E6671700899322 /* SDL_windowevents.c */,
 			);
-			name = events;
-			path = ../../src/events;
-			sourceTree = SOURCE_ROOT;
+			path = events;
+			sourceTree = "<group>";
 		};
 		04BDFDEC12E6671700899322 /* file */ = {
 			isa = PBXGroup;
@@ -1499,9 +1546,8 @@
 				04BDFDED12E6671700899322 /* cocoa */,
 				04BDFDF012E6671700899322 /* SDL_rwops.c */,
 			);
-			name = file;
-			path = ../../src/file;
-			sourceTree = SOURCE_ROOT;
+			path = file;
+			sourceTree = "<group>";
 		};
 		04BDFDED12E6671700899322 /* cocoa */ = {
 			isa = PBXGroup;
@@ -1520,9 +1566,8 @@
 				04BDFDFB12E6671700899322 /* SDL_haptic_c.h */,
 				04BDFDFC12E6671700899322 /* SDL_syshaptic.h */,
 			);
-			name = haptic;
-			path = ../../src/haptic;
-			sourceTree = SOURCE_ROOT;
+			path = haptic;
+			sourceTree = "<group>";
 		};
 		04BDFDF212E6671700899322 /* darwin */ = {
 			isa = PBXGroup;
@@ -1536,15 +1581,15 @@
 		04BDFDFF12E6671700899322 /* joystick */ = {
 			isa = PBXGroup;
 			children = (
+				A704170C20F09AA600A82227 /* hidapi */,
 				04BDFE0612E6671700899322 /* darwin */,
 				04BDFE1612E6671700899322 /* SDL_joystick.c */,
 				04BDFE1712E6671700899322 /* SDL_joystick_c.h */,
 				BBFC088A164C6514003E6A99 /* SDL_gamecontroller.c */,
 				04BDFE1812E6671700899322 /* SDL_sysjoystick.h */,
 			);
-			name = joystick;
-			path = ../../src/joystick;
-			sourceTree = SOURCE_ROOT;
+			path = joystick;
+			sourceTree = "<group>";
 		};
 		04BDFE0612E6671700899322 /* darwin */ = {
 			isa = PBXGroup;
@@ -1560,9 +1605,8 @@
 			children = (
 				04BDFE3212E6671700899322 /* dlopen */,
 			);
-			name = loadso;
-			path = ../../src/loadso;
-			sourceTree = SOURCE_ROOT;
+			path = loadso;
+			sourceTree = "<group>";
 		};
 		04BDFE3212E6671700899322 /* dlopen */ = {
 			isa = PBXGroup;
@@ -1578,9 +1622,8 @@
 				04BDFE4A12E6671700899322 /* macosx */,
 				04BDFE4E12E6671700899322 /* SDL_power.c */,
 			);
-			name = power;
-			path = ../../src/power;
-			sourceTree = SOURCE_ROOT;
+			path = power;
+			sourceTree = "<group>";
 		};
 		04BDFE4A12E6671700899322 /* macosx */ = {
 			isa = PBXGroup;
@@ -1600,9 +1643,8 @@
 				04BDFE6212E6671700899322 /* SDL_stdlib.c */,
 				04BDFE6312E6671700899322 /* SDL_string.c */,
 			);
-			name = stdlib;
-			path = ../../src/stdlib;
-			sourceTree = SOURCE_ROOT;
+			path = stdlib;
+			sourceTree = "<group>";
 		};
 		04BDFE6412E6671800899322 /* thread */ = {
 			isa = PBXGroup;
@@ -1612,9 +1654,8 @@
 				04BDFE8C12E6671800899322 /* SDL_thread.c */,
 				04BDFE8D12E6671800899322 /* SDL_thread_c.h */,
 			);
-			name = thread;
-			path = ../../src/thread;
-			sourceTree = SOURCE_ROOT;
+			path = thread;
+			sourceTree = "<group>";
 		};
 		04BDFE7D12E6671800899322 /* pthread */ = {
 			isa = PBXGroup;
@@ -1637,9 +1678,8 @@
 				04BDFE9F12E6671800899322 /* SDL_timer.c */,
 				04BDFEA012E6671800899322 /* SDL_timer_c.h */,
 			);
-			name = timer;
-			path = ../../src/timer;
-			sourceTree = SOURCE_ROOT;
+			path = timer;
+			sourceTree = "<group>";
 		};
 		04BDFEA112E6671800899322 /* unix */ = {
 			isa = PBXGroup;
@@ -1690,9 +1730,8 @@
 				AA9A7F141FB0209C00FED37F /* SDL_yuv_c.h */,
 				AA9A7F131FB0209C00FED37F /* SDL_yuv.c */,
 			);
-			name = video;
-			path = ../../src/video;
-			sourceTree = SOURCE_ROOT;
+			path = video;
+			sourceTree = "<group>";
 		};
 		04BDFEC112E6671800899322 /* cocoa */ = {
 			isa = PBXGroup;
@@ -1790,7 +1829,6 @@
 				08FB77ACFE841707C02AAC07 /* Library Source */,
 				034768DDFF38A45A11DB9C8B /* Products */,
 				BECDF66B0761BA81005FE872 /* Info-Framework.plist */,
-				BEC562FE0761C0E800A33029 /* Linked Frameworks */,
 				564624341FF821B70074AC87 /* Frameworks */,
 			);
 			comments = "To build Universal Binaries, we have experimented with a variety of different options.\nThe complication is that we must retain compatibility with at least 10.2. \nThe Universal Binary defaults only work for > 10.3.9\n\nSo far, we have found:\ngcc 4.0.0 with Xcode 2.1 always links against libgcc_s. gcc 4.0.1 from Xcode 2.2 fixes this problem.\n\nBut gcc 4.0 will not work with < 10.3.9 because we continue to get an undefined symbol to _fprintf$LDBL128.\nSo we must use gcc 3.3 on PPC to accomplish 10.2 support. (But 4.0 is required for i386.)\n\nSetting the deployment target to 10.4 will disable prebinding, so for PPC, we set it less than 10.4 to preserve prebinding for legacy support.\n\nSetting the PPC SDKROOT to /Developers/SDKs/MacOSX10.2.8.sdk will link to 63.0.0 libSystem.B.dylib. Leaving it at current or 10.4u links to 88.1.2. However, as long as we are using gcc 3.3, it doesn't seem to matter as testing has demonstrated both will run. We have decided not to invoke the 10.2.8 SDK because it is not a default installed component with Xcode which will probably cause most people problems. However, rather than deleting the SDKROOT_ppc entry entirely, we have mapped it to 10.4u in case we decide we need to change this setting.\n\nTo use Altivec or SSE, we needed architecture specific flags:\nOTHER_CFLAGS_ppc\nOTHER_CFLAGS_i386\nOTHER_CFLAGS=$(OTHER_CFLAGS_($CURRENT_ARCH))\n\nThe general OTHER_CFLAGS needed to be manually mapped to architecture specific options because Xcode didn't do this automatically for us.\n\n\n";
@@ -1799,13 +1837,6 @@
 			sourceTree = "<group>";
 			tabWidth = 4;
 			usesTabs = 0;
-		};
-		089C1665FE841158C02AAC07 /* Resources */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			name = Resources;
-			sourceTree = "<group>";
 		};
 		08FB77ACFE841707C02AAC07 /* Library Source */ = {
 			isa = PBXGroup;
@@ -1818,10 +1849,12 @@
 				567E2F1F17C44BBB005F1892 /* filesystem */,
 				04BDFDEC12E6671700899322 /* file */,
 				04BDFDF112E6671700899322 /* haptic */,
+				A73EBCD520F099C10043B449 /* hidapi */,
 				04BDFDFF12E6671700899322 /* joystick */,
 				04BDFE2F12E6671700899322 /* loadso */,
 				04BDFE4512E6671700899322 /* power */,
 				041B2C9712FA0D680087D585 /* render */,
+				F3950CDB212BC8BC00F51292 /* sensor */,
 				04BDFE5D12E6671700899322 /* stdlib */,
 				04BDFE6412E6671800899322 /* thread */,
 				04BDFE9512E6671800899322 /* timer */,
@@ -1838,11 +1871,20 @@
 				04BDFE5C12E6671700899322 /* SDL.c */,
 			);
 			name = "Library Source";
+			path = ../../src;
 			sourceTree = "<group>";
 		};
 		564624341FF821B70074AC87 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				A7381E931D8B69C300B177DD /* AudioToolbox.framework */,
+				007317C10858E15000B2BC32 /* Carbon.framework */,
+				0073179D0858DECD00B2BC32 /* Cocoa.framework */,
+				A7381E951D8B69D600B177DD /* CoreAudio.framework */,
+				00D0D08310675DD9004B05EF /* CoreFoundation.framework */,
+				FA73671C19A540EF004122E4 /* CoreVideo.framework */,
+				00CFA89C106B4BA100758660 /* ForceFeedback.framework */,
+				0073179F0858DECD00B2BC32 /* IOKit.framework */,
 				564624371FF821CB0074AC87 /* Metal.framework */,
 				564624351FF821B80074AC87 /* QuartzCore.framework */,
 			);
@@ -1854,7 +1896,7 @@
 			children = (
 				567E2F1B17C44BB2005F1892 /* SDL_sysfilesystem.m */,
 			);
-			name = filesystem;
+			path = filesystem;
 			sourceTree = "<group>";
 		};
 		56A6701C1856549B0007D20F /* dynapi */ = {
@@ -1865,7 +1907,7 @@
 				56A6701F185654B40007D20F /* SDL_dynapi.h */,
 				56A67020185654B40007D20F /* SDL_dynapi_overrides.h */,
 			);
-			name = dynapi;
+			path = dynapi;
 			sourceTree = "<group>";
 		};
 		5C2EF6921FC986D8003F5197 /* opengles2 */ = {
@@ -1879,15 +1921,44 @@
 			path = opengles2;
 			sourceTree = "<group>";
 		};
+		A704170720F09A6700A82227 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				A704170820F09A9800A82227 /* hid.c */,
+			);
+			path = mac;
+			sourceTree = "<group>";
+		};
+		A704170C20F09AA600A82227 /* hidapi */ = {
+			isa = PBXGroup;
+			children = (
+				A704171020F09AC900A82227 /* controller_type.h */,
+				A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */,
+				A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */,
+				A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */,
+				A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */,
+				A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */,
+				A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */,
+			);
+			path = hidapi;
+			sourceTree = "<group>";
+		};
+		A73EBCD520F099C10043B449 /* hidapi */ = {
+			isa = PBXGroup;
+			children = (
+				A704170720F09A6700A82227 /* mac */,
+			);
+			path = hidapi;
+			sourceTree = "<group>";
+		};
 		AA9A7F0E1FB0200B00FED37F /* yuv2rgb */ = {
 			isa = PBXGroup;
 			children = (
 				AA9A7F101FB0206300FED37F /* yuv_rgb.c */,
 				AA9A7F0F1FB0206300FED37F /* yuv_rgb.h */,
 			);
-			name = yuv2rgb;
-			path = ../../src/video/yuv2rgb;
-			sourceTree = SOURCE_ROOT;
+			path = yuv2rgb;
+			sourceTree = "<group>";
 		};
 		AADC5A401FDA030E00960936 /* metal */ = {
 			isa = PBXGroup;
@@ -1898,22 +1969,26 @@
 			path = metal;
 			sourceTree = "<group>";
 		};
-		BEC562FE0761C0E800A33029 /* Linked Frameworks */ = {
+		F30D9C8D212CABB40047DF2E /* dummy */ = {
 			isa = PBXGroup;
 			children = (
-				565AF9691FF823840077498A /* QuartzCore.framework */,
-				565AF9681FF8237A0077498A /* Metal.framework */,
-				A7381E931D8B69C300B177DD /* AudioToolbox.framework */,
-				007317C10858E15000B2BC32 /* Carbon.framework */,
-				0073179D0858DECD00B2BC32 /* Cocoa.framework */,
-				A7381E951D8B69D600B177DD /* CoreAudio.framework */,
-				00D0D08310675DD9004B05EF /* CoreFoundation.framework */,
-				FA73671C19A540EF004122E4 /* CoreVideo.framework */,
-				00CFA89C106B4BA100758660 /* ForceFeedback.framework */,
-				0073179F0858DECD00B2BC32 /* IOKit.framework */,
+				F30D9C8F212CABDB0047DF2E /* SDL_dummysensor.c */,
+				F30D9C8E212CABDB0047DF2E /* SDL_dummysensor.h */,
 			);
-			name = "Linked Frameworks";
+			path = dummy;
 			sourceTree = "<group>";
+		};
+		F3950CDB212BC8BC00F51292 /* sensor */ = {
+			isa = PBXGroup;
+			children = (
+				F30D9C8D212CABB40047DF2E /* dummy */,
+				F30D9C81212BC94E0047DF2E /* SDL_sensor_c.h */,
+				F30D9C83212BC94F0047DF2E /* SDL_sensor.c */,
+				F30D9C82212BC94F0047DF2E /* SDL_syssensor.h */,
+			);
+			name = sensor;
+			path = ../../src/sensor;
+			sourceTree = SOURCE_ROOT;
 		};
 		F59C70FC00D5CB5801000001 /* pkg-support */ = {
 			isa = PBXGroup;
@@ -1946,6 +2021,7 @@
 				AA75585E1595D4D800BBD41B /* SDL.h in Headers */,
 				AA7557FE1595D4D800BBD41B /* SDL_assert.h in Headers */,
 				AA7558001595D4D800BBD41B /* SDL_atomic.h in Headers */,
+				F30D9C87212BC94F0047DF2E /* SDL_syssensor.h in Headers */,
 				AA7558021595D4D800BBD41B /* SDL_audio.h in Headers */,
 				AADA5B8716CCAB3000107CF7 /* SDL_bits.h in Headers */,
 				AA7558041595D4D800BBD41B /* SDL_blendmode.h in Headers */,
@@ -1966,7 +2042,9 @@
 				AA75581A1595D4D800BBD41B /* SDL_hints.h in Headers */,
 				AA75581E1595D4D800BBD41B /* SDL_joystick.h in Headers */,
 				AA7558201595D4D800BBD41B /* SDL_keyboard.h in Headers */,
+				F3950CD8212BC88D00F51292 /* SDL_sensor.h in Headers */,
 				AA7558221595D4D800BBD41B /* SDL_keycode.h in Headers */,
+				F30D9C84212BC94F0047DF2E /* SDL_sensor_c.h in Headers */,
 				AA7558241595D4D800BBD41B /* SDL_loadso.h in Headers */,
 				AA7558261595D4D800BBD41B /* SDL_log.h in Headers */,
 				5C2EF6F91FC9EE35003F5197 /* SDL_egl_c.h in Headers */,
@@ -1993,6 +2071,7 @@
 				AA7558421595D4D800BBD41B /* SDL_revision.h in Headers */,
 				AA7558441595D4D800BBD41B /* SDL_rwops.h in Headers */,
 				AA7558461595D4D800BBD41B /* SDL_scancode.h in Headers */,
+				A704171720F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
 				AA7558481595D4D800BBD41B /* SDL_shape.h in Headers */,
 				AA75584A1595D4D800BBD41B /* SDL_stdinc.h in Headers */,
 				AA75584C1595D4D800BBD41B /* SDL_surface.h in Headers */,
@@ -2066,6 +2145,7 @@
 				04BD019912E6671800899322 /* SDL_shape_internals.h in Headers */,
 				04BD019C12E6671800899322 /* SDL_sysvideo.h in Headers */,
 				04BD01DC12E6671800899322 /* imKStoUCS.h in Headers */,
+				F30D9CCD212EB4810047DF2E /* SDL_displayevents_c.h in Headers */,
 				4D16644E1EDD6023003DE88E /* SDL_vulkan_internal.h in Headers */,
 				04BD01DE12E6671800899322 /* SDL_x11clipboard.h in Headers */,
 				04BD01E012E6671800899322 /* SDL_x11dyn.h in Headers */,
@@ -2083,6 +2163,7 @@
 				04BD01F912E6671800899322 /* SDL_x11window.h in Headers */,
 				041B2CA612FA0D680087D585 /* SDL_sysrender.h in Headers */,
 				AA9A7F161FB0209D00FED37F /* SDL_yuv_c.h in Headers */,
+				A704171D20F09AC900A82227 /* controller_type.h in Headers */,
 				04409B9312FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */,
 				04F7803912FB748500FC43C0 /* SDL_nullframebuffer_c.h in Headers */,
 				04F7804A12FB74A200FC43C0 /* SDL_blendfillrect.h in Headers */,
@@ -2090,6 +2171,7 @@
 				04F7804E12FB74A200FC43C0 /* SDL_blendpoint.h in Headers */,
 				56A67027185654B40007D20F /* SDL_dynapi.h in Headers */,
 				04F7804F12FB74A200FC43C0 /* SDL_draw.h in Headers */,
+				F30D9C90212CABDC0047DF2E /* SDL_dummysensor.h in Headers */,
 				04F7805112FB74A200FC43C0 /* SDL_drawline.h in Headers */,
 				04F7805312FB74A200FC43C0 /* SDL_drawpoint.h in Headers */,
 				0442EC1C12FE1BCB004C9285 /* SDL_render_sw_c.h in Headers */,
@@ -2119,6 +2201,7 @@
 				AA7558051595D4D800BBD41B /* SDL_blendmode.h in Headers */,
 				AA7558071595D4D800BBD41B /* SDL_clipboard.h in Headers */,
 				AA75580B1595D4D800BBD41B /* SDL_config.h in Headers */,
+				F30D9C85212BC94F0047DF2E /* SDL_sensor_c.h in Headers */,
 				AA7558091595D4D800BBD41B /* SDL_config_macosx.h in Headers */,
 				AA75580D1595D4D800BBD41B /* SDL_copying.h in Headers */,
 				AA75580F1595D4D800BBD41B /* SDL_cpuinfo.h in Headers */,
@@ -2132,6 +2215,7 @@
 				AA75581F1595D4D800BBD41B /* SDL_joystick.h in Headers */,
 				AA7558211595D4D800BBD41B /* SDL_keyboard.h in Headers */,
 				AA7558231595D4D800BBD41B /* SDL_keycode.h in Headers */,
+				A704171E20F09AC900A82227 /* controller_type.h in Headers */,
 				AA7558251595D4D800BBD41B /* SDL_loadso.h in Headers */,
 				AA7558271595D4D800BBD41B /* SDL_log.h in Headers */,
 				AA7558291595D4D800BBD41B /* SDL_main.h in Headers */,
@@ -2156,12 +2240,14 @@
 				AA7558491595D4D800BBD41B /* SDL_shape.h in Headers */,
 				56F9D55E1DF73B7C00C15B5D /* SDL_dataqueue.h in Headers */,
 				56A6702B185654B40007D20F /* SDL_dynapi_overrides.h in Headers */,
+				F30D9CCE212EB4810047DF2E /* SDL_displayevents_c.h in Headers */,
 				AA75584B1595D4D800BBD41B /* SDL_stdinc.h in Headers */,
 				AA75584D1595D4D800BBD41B /* SDL_surface.h in Headers */,
 				AA75584F1595D4D800BBD41B /* SDL_system.h in Headers */,
 				AA7558511595D4D800BBD41B /* SDL_syswm.h in Headers */,
 				AAC070FA195606770073DCDF /* SDL_opengl_glext.h in Headers */,
 				AA7558531595D4D800BBD41B /* SDL_thread.h in Headers */,
+				F30D9C88212BC94F0047DF2E /* SDL_syssensor.h in Headers */,
 				AA7558551595D4D800BBD41B /* SDL_timer.h in Headers */,
 				AA7558571595D4D800BBD41B /* SDL_touch.h in Headers */,
 				AA7558591595D4D800BBD41B /* SDL_types.h in Headers */,
@@ -2196,17 +2282,20 @@
 				04BD027212E6671800899322 /* SDL_windowevents_c.h in Headers */,
 				04BD027312E6671800899322 /* SDL_rwopsbundlesupport.h in Headers */,
 				5C2EF6FC1FC9EE64003F5197 /* SDL_rect_c.h in Headers */,
+				F30D9C91212CABDC0047DF2E /* SDL_dummysensor.h in Headers */,
 				04BD027B12E6671800899322 /* SDL_haptic_c.h in Headers */,
 				04BD027C12E6671800899322 /* SDL_syshaptic.h in Headers */,
 				04BD028212E6671800899322 /* SDL_sysjoystick_c.h in Headers */,
 				04BD028C12E6671800899322 /* SDL_joystick_c.h in Headers */,
 				04BD028D12E6671800899322 /* SDL_sysjoystick.h in Headers */,
+				F3950CD9212BC88D00F51292 /* SDL_sensor.h in Headers */,
 				04BD02B512E6671800899322 /* SDL_assert_c.h in Headers */,
 				04BD02B812E6671800899322 /* SDL_error_c.h in Headers */,
 				04BD02D912E6671800899322 /* SDL_sysmutex_c.h in Headers */,
 				04BD02DC12E6671800899322 /* SDL_systhread_c.h in Headers */,
 				04BD02E312E6671800899322 /* SDL_systhread.h in Headers */,
 				04BD02E512E6671800899322 /* SDL_thread_c.h in Headers */,
+				A704171820F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
 				04BD02F212E6671800899322 /* SDL_timer_c.h in Headers */,
 				04BD030D12E6671800899322 /* SDL_cocoaclipboard.h in Headers */,
 				04BD030F12E6671800899322 /* SDL_cocoaevents.h in Headers */,
@@ -2283,6 +2372,7 @@
 				DB313FCD17554B71006C0E22 /* SDL_blendmode.h in Headers */,
 				DB313FCE17554B71006C0E22 /* SDL_clipboard.h in Headers */,
 				DB313FD017554B71006C0E22 /* SDL_config.h in Headers */,
+				F30D9C86212BC94F0047DF2E /* SDL_sensor_c.h in Headers */,
 				DB313FCF17554B71006C0E22 /* SDL_config_macosx.h in Headers */,
 				DB313FD117554B71006C0E22 /* SDL_copying.h in Headers */,
 				DB313FD217554B71006C0E22 /* SDL_cpuinfo.h in Headers */,
@@ -2296,6 +2386,7 @@
 				DB313FD917554B71006C0E22 /* SDL_joystick.h in Headers */,
 				DB313FDA17554B71006C0E22 /* SDL_keyboard.h in Headers */,
 				DB313FDB17554B71006C0E22 /* SDL_keycode.h in Headers */,
+				A704171F20F09AC900A82227 /* controller_type.h in Headers */,
 				DB313FDC17554B71006C0E22 /* SDL_loadso.h in Headers */,
 				DB313FDD17554B71006C0E22 /* SDL_log.h in Headers */,
 				DB313FDE17554B71006C0E22 /* SDL_main.h in Headers */,
@@ -2320,12 +2411,14 @@
 				DB313FEE17554B71006C0E22 /* SDL_shape.h in Headers */,
 				56F9D55F1DF73B7D00C15B5D /* SDL_dataqueue.h in Headers */,
 				56A6702C185654B40007D20F /* SDL_dynapi_overrides.h in Headers */,
+				F30D9CCF212EB4810047DF2E /* SDL_displayevents_c.h in Headers */,
 				DB313FEF17554B71006C0E22 /* SDL_stdinc.h in Headers */,
 				DB313FF017554B71006C0E22 /* SDL_surface.h in Headers */,
 				DB313FF117554B71006C0E22 /* SDL_system.h in Headers */,
 				DB313FF217554B71006C0E22 /* SDL_syswm.h in Headers */,
 				AAC070FB195606770073DCDF /* SDL_opengl_glext.h in Headers */,
 				DB313FF317554B71006C0E22 /* SDL_thread.h in Headers */,
+				F30D9C89212BC94F0047DF2E /* SDL_syssensor.h in Headers */,
 				DB313FF417554B71006C0E22 /* SDL_timer.h in Headers */,
 				DB313FF517554B71006C0E22 /* SDL_touch.h in Headers */,
 				DB313FF617554B71006C0E22 /* SDL_types.h in Headers */,
@@ -2360,17 +2453,20 @@
 				DB313F8817554B71006C0E22 /* SDL_windowevents_c.h in Headers */,
 				DB313F8917554B71006C0E22 /* SDL_rwopsbundlesupport.h in Headers */,
 				5C2EF6FF1FC9EE65003F5197 /* SDL_rect_c.h in Headers */,
+				F30D9C92212CABDC0047DF2E /* SDL_dummysensor.h in Headers */,
 				DB313F8A17554B71006C0E22 /* SDL_haptic_c.h in Headers */,
 				DB313F8B17554B71006C0E22 /* SDL_syshaptic.h in Headers */,
 				DB313F8C17554B71006C0E22 /* SDL_sysjoystick_c.h in Headers */,
 				DB313F8D17554B71006C0E22 /* SDL_joystick_c.h in Headers */,
 				DB313F8E17554B71006C0E22 /* SDL_sysjoystick.h in Headers */,
+				F3950CDA212BC88D00F51292 /* SDL_sensor.h in Headers */,
 				DB313F8F17554B71006C0E22 /* SDL_assert_c.h in Headers */,
 				DB313F9017554B71006C0E22 /* SDL_error_c.h in Headers */,
 				DB313F9217554B71006C0E22 /* SDL_sysmutex_c.h in Headers */,
 				DB313F9317554B71006C0E22 /* SDL_systhread_c.h in Headers */,
 				DB313F9417554B71006C0E22 /* SDL_systhread.h in Headers */,
 				DB313F9517554B71006C0E22 /* SDL_thread_c.h in Headers */,
+				A704171920F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
 				DB313F9617554B71006C0E22 /* SDL_timer_c.h in Headers */,
 				DB313F9717554B71006C0E22 /* SDL_cocoaclipboard.h in Headers */,
 				DB313F9817554B71006C0E22 /* SDL_cocoaevents.h in Headers */,
@@ -2518,7 +2614,7 @@
 		0867D690FE84028FC02AAC07 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 0900;
+				LastUpgradeCheck = 1000;
 			};
 			buildConfigurationList = 0073178E0858DB0500B2BC32 /* Build configuration list for PBXProject "SDL" */;
 			compatibilityVersion = "Xcode 3.2";
@@ -2602,6 +2698,7 @@
 				04BD004112E6671800899322 /* SDL_cpuinfo.c in Sources */,
 				04BD004812E6671800899322 /* SDL_clipboardevents.c in Sources */,
 				04BD004A12E6671800899322 /* SDL_events.c in Sources */,
+				A704172620F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
 				04BD004C12E6671800899322 /* SDL_gesture.c in Sources */,
 				04BD004E12E6671800899322 /* SDL_keyboard.c in Sources */,
 				04BD005012E6671800899322 /* SDL_mouse.c in Sources */,
@@ -2640,6 +2737,7 @@
 				04BD00F612E6671800899322 /* SDL_cocoaevents.m in Sources */,
 				04BD00F812E6671800899322 /* SDL_cocoakeyboard.m in Sources */,
 				AA9A7F151FB0209D00FED37F /* SDL_yuv.c in Sources */,
+				A704171A20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
 				04BD00FA12E6671800899322 /* SDL_cocoamodes.m in Sources */,
 				4D16644F1EDD6023003DE88E /* SDL_vulkan_utils.c in Sources */,
 				04BD00FC12E6671800899322 /* SDL_cocoamouse.m in Sources */,
@@ -2648,6 +2746,7 @@
 				04BD010212E6671800899322 /* SDL_cocoavideo.m in Sources */,
 				04BD010412E6671800899322 /* SDL_cocoawindow.m in Sources */,
 				04BD011712E6671800899322 /* SDL_nullevents.c in Sources */,
+				A704172320F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
 				04BD011B12E6671800899322 /* SDL_nullvideo.c in Sources */,
 				04BD017512E6671800899322 /* SDL_blit.c in Sources */,
 				04BD017712E6671800899322 /* SDL_blit_0.c in Sources */,
@@ -2656,6 +2755,8 @@
 				04BD017912E6671800899322 /* SDL_blit_A.c in Sources */,
 				04BD017A12E6671800899322 /* SDL_blit_auto.c in Sources */,
 				04BD017C12E6671800899322 /* SDL_blit_copy.c in Sources */,
+				A704172020F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
+				A704170920F09A9800A82227 /* hid.c in Sources */,
 				04BD017E12E6671800899322 /* SDL_blit_N.c in Sources */,
 				04BD017F12E6671800899322 /* SDL_blit_slow.c in Sources */,
 				04BD018112E6671800899322 /* SDL_bmp.c in Sources */,
@@ -2664,6 +2765,7 @@
 				04BD018C12E6671800899322 /* SDL_pixels.c in Sources */,
 				04BD018E12E6671800899322 /* SDL_rect.c in Sources */,
 				04BD019612E6671800899322 /* SDL_RLEaccel.c in Sources */,
+				A704171420F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
 				04BD019812E6671800899322 /* SDL_shape.c in Sources */,
 				04BD019A12E6671800899322 /* SDL_stretch.c in Sources */,
 				04BD019B12E6671800899322 /* SDL_surface.c in Sources */,
@@ -2689,14 +2791,17 @@
 				AADC5A441FDA035D00960936 /* SDL_render_metal.m in Sources */,
 				04F7804B12FB74A200FC43C0 /* SDL_blendline.c in Sources */,
 				04F7804D12FB74A200FC43C0 /* SDL_blendpoint.c in Sources */,
+				F30D9C93212CABDC0047DF2E /* SDL_dummysensor.c in Sources */,
 				5C2EF6F81FC9EE35003F5197 /* SDL_egl.c in Sources */,
 				04F7805012FB74A200FC43C0 /* SDL_drawline.c in Sources */,
 				04F7805212FB74A200FC43C0 /* SDL_drawpoint.c in Sources */,
 				0442EC1812FE1BBA004C9285 /* SDL_render_gl.c in Sources */,
+				F30D9CD0212EB4810047DF2E /* SDL_displayevents.c in Sources */,
 				0442EC1D12FE1BCB004C9285 /* SDL_render_sw.c in Sources */,
 				0442EC5A12FE1C60004C9285 /* SDL_x11framebuffer.c in Sources */,
 				0442EC5F12FE1C75004C9285 /* SDL_hints.c in Sources */,
 				56A67024185654B40007D20F /* SDL_dynapi.c in Sources */,
+				F30D9C8A212BC94F0047DF2E /* SDL_sensor.c in Sources */,
 				04BAC0C81300C2160055DE28 /* SDL_log.c in Sources */,
 				5C2EF6EE1FC9D0ED003F5197 /* SDL_cocoaopengles.m in Sources */,
 				0435673E1303160F00BA5428 /* SDL_shaders_gl.c in Sources */,
@@ -2731,6 +2836,7 @@
 				04BD024812E6671800899322 /* SDL_audiotypecvt.c in Sources */,
 				04BD024912E6671800899322 /* SDL_mixer.c in Sources */,
 				04BD025112E6671800899322 /* SDL_wave.c in Sources */,
+				A704172720F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
 				04BD025C12E6671800899322 /* SDL_cpuinfo.c in Sources */,
 				04BD026312E6671800899322 /* SDL_clipboardevents.c in Sources */,
 				04BD026512E6671800899322 /* SDL_events.c in Sources */,
@@ -2769,6 +2875,7 @@
 				04BD02E412E6671800899322 /* SDL_thread.c in Sources */,
 				04BD02F112E6671800899322 /* SDL_timer.c in Sources */,
 				04BD02F312E6671800899322 /* SDL_systimer.c in Sources */,
+				A704171B20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
 				04BD030E12E6671800899322 /* SDL_cocoaclipboard.m in Sources */,
 				04BD031012E6671800899322 /* SDL_cocoaevents.m in Sources */,
 				04BD031212E6671800899322 /* SDL_cocoakeyboard.m in Sources */,
@@ -2777,6 +2884,7 @@
 				5C2EF6A31FC98B38003F5197 /* SDL_yuv.c in Sources */,
 				5C2EF6F11FC9D181003F5197 /* SDL_cocoaopengles.m in Sources */,
 				04BD031812E6671800899322 /* SDL_cocoaopengl.m in Sources */,
+				A704172420F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
 				04BD031A12E6671800899322 /* SDL_cocoashape.m in Sources */,
 				04BD031C12E6671800899322 /* SDL_cocoavideo.m in Sources */,
 				04BD031E12E6671800899322 /* SDL_cocoawindow.m in Sources */,
@@ -2785,6 +2893,8 @@
 				5C2EF6A51FC98B6B003F5197 /* yuv_rgb.c in Sources */,
 				04BD038F12E6671800899322 /* SDL_blit.c in Sources */,
 				04BD039112E6671800899322 /* SDL_blit_0.c in Sources */,
+				A704172120F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
+				A704170A20F09A9800A82227 /* hid.c in Sources */,
 				04BD039212E6671800899322 /* SDL_blit_1.c in Sources */,
 				04BD039312E6671800899322 /* SDL_blit_A.c in Sources */,
 				04BD039412E6671800899322 /* SDL_blit_auto.c in Sources */,
@@ -2793,6 +2903,7 @@
 				04BD039912E6671800899322 /* SDL_blit_slow.c in Sources */,
 				04BD039B12E6671800899322 /* SDL_bmp.c in Sources */,
 				04BD039C12E6671800899322 /* SDL_clipboard.c in Sources */,
+				A704171520F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
 				04BD03A112E6671800899322 /* SDL_fillrect.c in Sources */,
 				04BD03A612E6671800899322 /* SDL_pixels.c in Sources */,
 				04BD03A812E6671800899322 /* SDL_rect.c in Sources */,
@@ -2818,14 +2929,17 @@
 				AADC5A451FDA047900960936 /* SDL_render_metal.m in Sources */,
 				041B2CAB12FA0D680087D585 /* SDL_render.c in Sources */,
 				04409B9812FA97ED00FB9AA8 /* SDL_yuv_sw.c in Sources */,
+				F30D9C94212CABDC0047DF2E /* SDL_dummysensor.c in Sources */,
 				04F7803C12FB748500FC43C0 /* SDL_nullframebuffer.c in Sources */,
 				04F7805512FB74A200FC43C0 /* SDL_blendfillrect.c in Sources */,
 				04F7805712FB74A200FC43C0 /* SDL_blendline.c in Sources */,
 				04F7805912FB74A200FC43C0 /* SDL_blendpoint.c in Sources */,
+				F30D9CD1212EB4810047DF2E /* SDL_displayevents.c in Sources */,
 				04F7805C12FB74A200FC43C0 /* SDL_drawline.c in Sources */,
 				04F7805E12FB74A200FC43C0 /* SDL_drawpoint.c in Sources */,
 				0442EC1912FE1BBA004C9285 /* SDL_render_gl.c in Sources */,
 				0442EC1F12FE1BCB004C9285 /* SDL_render_sw.c in Sources */,
+				F30D9C8B212BC94F0047DF2E /* SDL_sensor.c in Sources */,
 				56A67025185654B40007D20F /* SDL_dynapi.c in Sources */,
 				0442EC5C12FE1C60004C9285 /* SDL_x11framebuffer.c in Sources */,
 				0442EC6012FE1C75004C9285 /* SDL_hints.c in Sources */,
@@ -2860,6 +2974,7 @@
 				DB31400617554B71006C0E22 /* SDL_audiotypecvt.c in Sources */,
 				DB31400717554B71006C0E22 /* SDL_mixer.c in Sources */,
 				DB31400817554B71006C0E22 /* SDL_wave.c in Sources */,
+				A704172820F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
 				DB31400917554B71006C0E22 /* SDL_cpuinfo.c in Sources */,
 				DB31400A17554B71006C0E22 /* SDL_clipboardevents.c in Sources */,
 				DB31400B17554B71006C0E22 /* SDL_events.c in Sources */,
@@ -2898,6 +3013,7 @@
 				DB31402B17554B71006C0E22 /* SDL_thread.c in Sources */,
 				DB31402C17554B71006C0E22 /* SDL_timer.c in Sources */,
 				DB31402D17554B71006C0E22 /* SDL_systimer.c in Sources */,
+				A704171C20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
 				DB31402E17554B71006C0E22 /* SDL_cocoaclipboard.m in Sources */,
 				DB31402F17554B71006C0E22 /* SDL_cocoaevents.m in Sources */,
 				DB31403017554B71006C0E22 /* SDL_cocoakeyboard.m in Sources */,
@@ -2906,6 +3022,7 @@
 				5C2EF6A41FC98B39003F5197 /* SDL_yuv.c in Sources */,
 				5C2EF6F31FC9D182003F5197 /* SDL_cocoaopengles.m in Sources */,
 				DB31403317554B71006C0E22 /* SDL_cocoaopengl.m in Sources */,
+				A704172520F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
 				DB31403417554B71006C0E22 /* SDL_cocoashape.m in Sources */,
 				DB31403517554B71006C0E22 /* SDL_cocoavideo.m in Sources */,
 				DB31403617554B71006C0E22 /* SDL_cocoawindow.m in Sources */,
@@ -2914,6 +3031,8 @@
 				5C2EF6A61FC98B6C003F5197 /* yuv_rgb.c in Sources */,
 				DB31403917554B71006C0E22 /* SDL_blit.c in Sources */,
 				DB31403A17554B71006C0E22 /* SDL_blit_0.c in Sources */,
+				A704172220F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
+				A704170B20F09A9800A82227 /* hid.c in Sources */,
 				DB31403B17554B71006C0E22 /* SDL_blit_1.c in Sources */,
 				DB31403C17554B71006C0E22 /* SDL_blit_A.c in Sources */,
 				DB31403D17554B71006C0E22 /* SDL_blit_auto.c in Sources */,
@@ -2922,6 +3041,7 @@
 				DB31404017554B71006C0E22 /* SDL_blit_slow.c in Sources */,
 				DB31404117554B71006C0E22 /* SDL_bmp.c in Sources */,
 				DB31404217554B71006C0E22 /* SDL_clipboard.c in Sources */,
+				A704171620F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
 				DB31404317554B71006C0E22 /* SDL_fillrect.c in Sources */,
 				DB31404417554B71006C0E22 /* SDL_pixels.c in Sources */,
 				DB31404517554B71006C0E22 /* SDL_rect.c in Sources */,
@@ -2947,14 +3067,17 @@
 				AADC5A481FDA048100960936 /* SDL_render_metal.m in Sources */,
 				DB31405817554B71006C0E22 /* SDL_render.c in Sources */,
 				DB31405A17554B71006C0E22 /* SDL_yuv_sw.c in Sources */,
+				F30D9C95212CABDC0047DF2E /* SDL_dummysensor.c in Sources */,
 				DB31405B17554B71006C0E22 /* SDL_nullframebuffer.c in Sources */,
 				DB31405C17554B71006C0E22 /* SDL_blendfillrect.c in Sources */,
 				DB31405D17554B71006C0E22 /* SDL_blendline.c in Sources */,
 				DB31405E17554B71006C0E22 /* SDL_blendpoint.c in Sources */,
+				F30D9CD2212EB4810047DF2E /* SDL_displayevents.c in Sources */,
 				DB31405F17554B71006C0E22 /* SDL_drawline.c in Sources */,
 				DB31406017554B71006C0E22 /* SDL_drawpoint.c in Sources */,
 				DB31406117554B71006C0E22 /* SDL_render_gl.c in Sources */,
 				DB31406217554B71006C0E22 /* SDL_render_sw.c in Sources */,
+				F30D9C8C212BC94F0047DF2E /* SDL_sensor.c in Sources */,
 				56A67026185654B40007D20F /* SDL_dynapi.c in Sources */,
 				DB31406317554B71006C0E22 /* SDL_x11framebuffer.c in Sources */,
 				DB31406417554B71006C0E22 /* SDL_hints.c in Sources */,
@@ -2984,16 +3107,19 @@
 		00CFA621106A567900758660 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 				CLANG_WARN_STRICT_PROTOTYPES = YES;
@@ -3019,6 +3145,7 @@
 					/usr/X11R6/include,
 					"$(VULKAN_SDK)/include",
 					../../src/video/khronos,
+					../../src/hidapi/hidapi,
 				);
 				MACOSX_DEPLOYMENT_TARGET = 10.6;
 				SDKROOT = macosx;
@@ -3029,11 +3156,11 @@
 		00CFA622106A567900758660 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_LINK_OBJC_RUNTIME = NO;
 				COMBINE_HIDPI_IMAGES = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1.0.0;
-				DYLIB_CURRENT_VERSION = 8.0.0;
+				DYLIB_CURRENT_VERSION = 10.0.0;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)",
@@ -3041,10 +3168,7 @@
 				FRAMEWORK_VERSION = A;
 				INFOPLIST_FILE = "Info-Framework.plist";
 				INSTALL_PATH = "@rpath";
-				OTHER_LDFLAGS = (
-					"-Wl,-weak_framework,Metal",
-					"-liconv",
-				);
+				OTHER_LDFLAGS = "-liconv";
 				PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL2;
 				PRODUCT_NAME = SDL2;
 				PROVISIONING_PROFILE = "";
@@ -3055,6 +3179,7 @@
 		00CFA623106A567900758660 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				COMBINE_HIDPI_IMAGES = YES;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(GCC_PREPROCESSOR_DEFINITIONS)",
@@ -3072,6 +3197,7 @@
 		00CFA625106A567900758660 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				PRODUCT_NAME = "Standard DMG";
 				PROVISIONING_PROFILE = "";
 			};
@@ -3080,16 +3206,19 @@
 		00CFA627106A568900758660 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_COMMA = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INFINITE_RECURSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 				CLANG_WARN_STRICT_PROTOTYPES = YES;
@@ -3114,6 +3243,7 @@
 					/usr/X11R6/include,
 					"$(VULKAN_SDK)/include",
 					../../src/video/khronos,
+					../../src/hidapi/hidapi,
 				);
 				MACOSX_DEPLOYMENT_TARGET = 10.6;
 				ONLY_ACTIVE_ARCH = YES;
@@ -3125,11 +3255,11 @@
 		00CFA628106A568900758660 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_LINK_OBJC_RUNTIME = NO;
 				COMBINE_HIDPI_IMAGES = YES;
 				DYLIB_COMPATIBILITY_VERSION = 1.0.0;
-				DYLIB_CURRENT_VERSION = 8.0.0;
+				DYLIB_CURRENT_VERSION = 10.0.0;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)",
@@ -3137,10 +3267,7 @@
 				FRAMEWORK_VERSION = A;
 				INFOPLIST_FILE = "Info-Framework.plist";
 				INSTALL_PATH = "@rpath";
-				OTHER_LDFLAGS = (
-					"-Wl,-weak_framework,Metal",
-					"-liconv",
-				);
+				OTHER_LDFLAGS = "-liconv";
 				PRODUCT_BUNDLE_IDENTIFIER = org.libsdl.SDL2;
 				PRODUCT_NAME = SDL2;
 				PROVISIONING_PROFILE = "";
@@ -3151,6 +3278,7 @@
 		00CFA629106A568900758660 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				COMBINE_HIDPI_IMAGES = YES;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(GCC_PREPROCESSOR_DEFINITIONS)",
@@ -3168,6 +3296,7 @@
 		00CFA62B106A568900758660 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				PRODUCT_NAME = "Standard DMG";
 				PROVISIONING_PROFILE = "";
 			};
@@ -3176,6 +3305,7 @@
 		DB31407517554B71006C0E22 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				COMBINE_HIDPI_IMAGES = YES;
 				EXECUTABLE_PREFIX = lib;
 				GCC_PREPROCESSOR_DEFINITIONS = (
@@ -3195,6 +3325,7 @@
 		DB31407617554B71006C0E22 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				COMBINE_HIDPI_IMAGES = YES;
 				EXECUTABLE_PREFIX = lib;
 				GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/source/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj b/source/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj
old mode 100755
new mode 100644
index 1d91144..8c524cc
--- a/source/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj
+++ b/source/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj
@@ -1134,36 +1134,36 @@
 
 /* Begin PBXFileReference section */
 		0017958C10741F7900F5D044 /* testatomic */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testatomic; sourceTree = BUILT_PRODUCTS_DIR; };
-		0017958F1074216E00F5D044 /* testatomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testatomic.c; path = ../../test/testatomic.c; sourceTree = SOURCE_ROOT; };
+		0017958F1074216E00F5D044 /* testatomic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testatomic.c; sourceTree = "<group>"; };
 		001795AD107421BF00F5D044 /* testaudioinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testaudioinfo; sourceTree = BUILT_PRODUCTS_DIR; };
-		001795B01074222D00F5D044 /* testaudioinfo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testaudioinfo.c; path = ../../test/testaudioinfo.c; sourceTree = SOURCE_ROOT; };
+		001795B01074222D00F5D044 /* testaudioinfo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testaudioinfo.c; sourceTree = "<group>"; };
 		0017972110742F3200F5D044 /* testgl2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testgl2; sourceTree = BUILT_PRODUCTS_DIR; };
-		0017972710742FB900F5D044 /* testgl2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testgl2.c; path = ../../test/testgl2.c; sourceTree = SOURCE_ROOT; };
+		0017972710742FB900F5D044 /* testgl2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testgl2.c; sourceTree = "<group>"; };
 		00179748107430D600F5D044 /* testhaptic */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testhaptic; sourceTree = BUILT_PRODUCTS_DIR; };
-		0017974E1074315700F5D044 /* testhaptic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testhaptic.c; path = ../../test/testhaptic.c; sourceTree = SOURCE_ROOT; };
+		0017974E1074315700F5D044 /* testhaptic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testhaptic.c; sourceTree = "<group>"; };
 		0017976E107431B300F5D044 /* testdraw2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testdraw2; sourceTree = BUILT_PRODUCTS_DIR; };
-		001797711074320D00F5D044 /* testdraw2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testdraw2.c; path = ../../test/testdraw2.c; sourceTree = SOURCE_ROOT; };
+		001797711074320D00F5D044 /* testdraw2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testdraw2.c; sourceTree = "<group>"; };
 		0017978E107432AE00F5D044 /* testime */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testime; sourceTree = BUILT_PRODUCTS_DIR; };
-		00179791107432FA00F5D044 /* testime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testime.c; path = ../../test/testime.c; sourceTree = SOURCE_ROOT; };
+		00179791107432FA00F5D044 /* testime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testime.c; sourceTree = "<group>"; };
 		001797AE1074334C00F5D044 /* testintersections */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testintersections; sourceTree = BUILT_PRODUCTS_DIR; };
-		001797B31074339C00F5D044 /* testintersections.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testintersections.c; path = ../../test/testintersections.c; sourceTree = SOURCE_ROOT; };
+		001797B31074339C00F5D044 /* testintersections.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testintersections.c; sourceTree = "<group>"; };
 		001797D0107433C600F5D044 /* testloadso */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testloadso; sourceTree = BUILT_PRODUCTS_DIR; };
-		001797D31074343E00F5D044 /* testloadso.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testloadso.c; path = ../../test/testloadso.c; sourceTree = SOURCE_ROOT; };
+		001797D31074343E00F5D044 /* testloadso.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testloadso.c; sourceTree = "<group>"; };
 		001798121074355200F5D044 /* testmultiaudio */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testmultiaudio; sourceTree = BUILT_PRODUCTS_DIR; };
-		001798151074359B00F5D044 /* testmultiaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testmultiaudio.c; path = ../../test/testmultiaudio.c; sourceTree = SOURCE_ROOT; };
-		0017985A107436ED00F5D044 /* testnative.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testnative.c; path = ../../test/testnative.c; sourceTree = SOURCE_ROOT; };
-		0017985B107436ED00F5D044 /* testnative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = testnative.h; path = ../../test/testnative.h; sourceTree = SOURCE_ROOT; };
-		0017985C107436ED00F5D044 /* testnativecocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = testnativecocoa.m; path = ../../test/testnativecocoa.m; sourceTree = SOURCE_ROOT; };
-		00179872107438D000F5D044 /* testnativex11.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testnativex11.c; path = ../../test/testnativex11.c; sourceTree = SOURCE_ROOT; };
+		001798151074359B00F5D044 /* testmultiaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testmultiaudio.c; sourceTree = "<group>"; };
+		0017985A107436ED00F5D044 /* testnative.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testnative.c; sourceTree = "<group>"; };
+		0017985B107436ED00F5D044 /* testnative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = testnative.h; sourceTree = "<group>"; };
+		0017985C107436ED00F5D044 /* testnativecocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = testnativecocoa.m; sourceTree = "<group>"; };
+		00179872107438D000F5D044 /* testnativex11.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testnativex11.c; sourceTree = "<group>"; };
 		001798941074392D00F5D044 /* testnative */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testnative; sourceTree = BUILT_PRODUCTS_DIR; };
 		001798B5107439DF00F5D044 /* testpower */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testpower; sourceTree = BUILT_PRODUCTS_DIR; };
-		001798B910743A4900F5D044 /* testpower.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testpower.c; path = ../../test/testpower.c; sourceTree = SOURCE_ROOT; };
+		001798B910743A4900F5D044 /* testpower.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testpower.c; sourceTree = "<group>"; };
 		001798F210743BEC00F5D044 /* testresample */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testresample; sourceTree = BUILT_PRODUCTS_DIR; };
-		001798F910743E9200F5D044 /* testresample.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testresample.c; path = ../../test/testresample.c; sourceTree = SOURCE_ROOT; };
+		001798F910743E9200F5D044 /* testresample.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testresample.c; sourceTree = "<group>"; };
 		0017991610743F1000F5D044 /* testsprite2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testsprite2; sourceTree = BUILT_PRODUCTS_DIR; };
-		0017991910743F5300F5D044 /* testsprite2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testsprite2.c; path = ../../test/testsprite2.c; sourceTree = SOURCE_ROOT; };
+		0017991910743F5300F5D044 /* testsprite2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testsprite2.c; sourceTree = "<group>"; };
 		0017993810743FB700F5D044 /* testwm2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testwm2; sourceTree = BUILT_PRODUCTS_DIR; };
-		0017993B10743FEF00F5D044 /* testwm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testwm2.c; path = ../../test/testwm2.c; sourceTree = SOURCE_ROOT; };
+		0017993B10743FEF00F5D044 /* testwm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testwm2.c; sourceTree = "<group>"; };
 		002A863B10730545007319AE /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = "<absolute>"; };
 		002A863C10730545007319AE /* ForceFeedback.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ForceFeedback.framework; path = /System/Library/Frameworks/ForceFeedback.framework; sourceTree = "<absolute>"; };
 		002A863D10730545007319AE /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
@@ -1173,37 +1173,37 @@
 		002A873910730675007319AE /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
 		002F33A709CA188600EBEB88 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
 		002F341209CA1BFF00EBEB88 /* testfile */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testfile; sourceTree = BUILT_PRODUCTS_DIR; };
-		002F341709CA1C5B00EBEB88 /* testfile.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testfile.c; path = ../../test/testfile.c; sourceTree = SOURCE_ROOT; };
+		002F341709CA1C5B00EBEB88 /* testfile.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testfile.c; sourceTree = "<group>"; };
 		002F343109CA1F0300EBEB88 /* testiconv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testiconv; sourceTree = BUILT_PRODUCTS_DIR; };
-		002F343609CA1F6F00EBEB88 /* testiconv.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testiconv.c; path = ../../test/testiconv.c; sourceTree = SOURCE_ROOT; };
+		002F343609CA1F6F00EBEB88 /* testiconv.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testiconv.c; sourceTree = "<group>"; };
 		002F344D09CA1FB300EBEB88 /* testoverlay2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testoverlay2; sourceTree = BUILT_PRODUCTS_DIR; };
-		002F345209CA201C00EBEB88 /* testoverlay2.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testoverlay2.c; path = ../../test/testoverlay2.c; sourceTree = SOURCE_ROOT; };
+		002F345209CA201C00EBEB88 /* testoverlay2.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testoverlay2.c; sourceTree = "<group>"; };
 		002F346A09CA204F00EBEB88 /* testplatform */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testplatform; sourceTree = BUILT_PRODUCTS_DIR; };
-		002F346F09CA20A600EBEB88 /* testplatform.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testplatform.c; path = ../../test/testplatform.c; sourceTree = SOURCE_ROOT; };
+		002F346F09CA20A600EBEB88 /* testplatform.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testplatform.c; sourceTree = "<group>"; };
 		003FA63A093FFD41000C53B3 /* SDL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL.xcodeproj; path = ../SDL/SDL.xcodeproj; sourceTree = SOURCE_ROOT; };
-		00794E5D09D20839003FC8A1 /* icon.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = icon.bmp; path = ../../test/icon.bmp; sourceTree = SOURCE_ROOT; };
-		00794E5E09D20839003FC8A1 /* moose.dat */ = {isa = PBXFileReference; lastKnownFileType = file; name = moose.dat; path = ../../test/moose.dat; sourceTree = SOURCE_ROOT; };
-		00794E5F09D20839003FC8A1 /* picture.xbm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = picture.xbm; path = ../../test/picture.xbm; sourceTree = SOURCE_ROOT; };
-		00794E6109D20839003FC8A1 /* sample.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = sample.bmp; path = ../../test/sample.bmp; sourceTree = SOURCE_ROOT; };
-		00794E6209D20839003FC8A1 /* sample.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sample.wav; path = ../../test/sample.wav; sourceTree = SOURCE_ROOT; };
-		00794E6309D20839003FC8A1 /* utf8.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = utf8.txt; path = ../../test/utf8.txt; sourceTree = SOURCE_ROOT; };
-		083E4872006D84C97F000001 /* loopwave.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = loopwave.c; path = ../../test/loopwave.c; sourceTree = SOURCE_ROOT; };
-		083E4878006D85357F000001 /* testerror.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testerror.c; path = ../../test/testerror.c; sourceTree = SOURCE_ROOT; };
-		083E487E006D86A17F000001 /* testsem.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testsem.c; path = ../../test/testsem.c; sourceTree = SOURCE_ROOT; };
-		083E4880006D86A17F000001 /* testtimer.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testtimer.c; path = ../../test/testtimer.c; sourceTree = SOURCE_ROOT; };
-		083E4882006D86A17F000001 /* testver.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testver.c; path = ../../test/testver.c; sourceTree = SOURCE_ROOT; };
-		083E4887006D86A17F000001 /* torturethread.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = torturethread.c; path = ../../test/torturethread.c; sourceTree = SOURCE_ROOT; };
-		092D6D10FFB30A2C7F000001 /* checkkeys.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = checkkeys.c; path = ../../test/checkkeys.c; sourceTree = SOURCE_ROOT; };
-		092D6D58FFB311A97F000001 /* testthread.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testthread.c; path = ../../test/testthread.c; sourceTree = SOURCE_ROOT; };
-		092D6D62FFB312AA7F000001 /* testjoystick.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testjoystick.c; path = ../../test/testjoystick.c; sourceTree = SOURCE_ROOT; };
-		092D6D6CFFB313437F000001 /* testkeys.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testkeys.c; path = ../../test/testkeys.c; sourceTree = SOURCE_ROOT; };
-		092D6D75FFB313BB7F000001 /* testlock.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = testlock.c; path = ../../test/testlock.c; sourceTree = SOURCE_ROOT; };
+		00794E5D09D20839003FC8A1 /* icon.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = icon.bmp; sourceTree = "<group>"; };
+		00794E5E09D20839003FC8A1 /* moose.dat */ = {isa = PBXFileReference; lastKnownFileType = file; path = moose.dat; sourceTree = "<group>"; };
+		00794E5F09D20839003FC8A1 /* picture.xbm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = picture.xbm; sourceTree = "<group>"; };
+		00794E6109D20839003FC8A1 /* sample.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = sample.bmp; sourceTree = "<group>"; };
+		00794E6209D20839003FC8A1 /* sample.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = sample.wav; sourceTree = "<group>"; };
+		00794E6309D20839003FC8A1 /* utf8.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = utf8.txt; sourceTree = "<group>"; };
+		083E4872006D84C97F000001 /* loopwave.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = loopwave.c; sourceTree = "<group>"; };
+		083E4878006D85357F000001 /* testerror.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testerror.c; sourceTree = "<group>"; };
+		083E487E006D86A17F000001 /* testsem.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testsem.c; sourceTree = "<group>"; };
+		083E4880006D86A17F000001 /* testtimer.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testtimer.c; sourceTree = "<group>"; };
+		083E4882006D86A17F000001 /* testver.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testver.c; sourceTree = "<group>"; };
+		083E4887006D86A17F000001 /* torturethread.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = torturethread.c; sourceTree = "<group>"; };
+		092D6D10FFB30A2C7F000001 /* checkkeys.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = checkkeys.c; sourceTree = "<group>"; };
+		092D6D58FFB311A97F000001 /* testthread.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testthread.c; sourceTree = "<group>"; };
+		092D6D62FFB312AA7F000001 /* testjoystick.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testjoystick.c; sourceTree = "<group>"; };
+		092D6D6CFFB313437F000001 /* testkeys.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testkeys.c; sourceTree = "<group>"; };
+		092D6D75FFB313BB7F000001 /* testlock.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = testlock.c; sourceTree = "<group>"; };
 		4537749212091504002F0F45 /* testshape */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testshape; sourceTree = BUILT_PRODUCTS_DIR; };
-		453774A4120915E3002F0F45 /* testshape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testshape.c; path = ../../test/testshape.c; sourceTree = SOURCE_ROOT; };
+		453774A4120915E3002F0F45 /* testshape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testshape.c; sourceTree = "<group>"; };
 		66E88E5B203B733C0004D44E /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
-		66E88E8A203B778F0004D44E /* testyuv_cvt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testyuv_cvt.c; path = ../../test/testyuv_cvt.c; sourceTree = "<group>"; };
-		AAF02FF41F90089800B9A9FB /* SDL_test_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_memory.c; path = ../../src/test/SDL_test_memory.c; sourceTree = "<group>"; };
-		BBFC088E164C6820003E6A99 /* testgamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testgamecontroller.c; path = ../../test/testgamecontroller.c; sourceTree = "<group>"; };
+		66E88E8A203B778F0004D44E /* testyuv_cvt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testyuv_cvt.c; sourceTree = "<group>"; };
+		AAF02FF41F90089800B9A9FB /* SDL_test_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_memory.c; sourceTree = "<group>"; };
+		BBFC088E164C6820003E6A99 /* testgamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testgamecontroller.c; sourceTree = "<group>"; };
 		BBFC08CD164C6862003E6A99 /* testgamecontroller */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testgamecontroller; sourceTree = BUILT_PRODUCTS_DIR; };
 		BEC566B60761D90300A33029 /* checkkeys */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = checkkeys; sourceTree = BUILT_PRODUCTS_DIR; };
 		BEC566D10761D90300A33029 /* loopwave */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = loopwave; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1216,37 +1216,37 @@
 		BEC567980761D90500A33029 /* testtimer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testtimer; sourceTree = BUILT_PRODUCTS_DIR; };
 		BEC567B20761D90500A33029 /* testversion */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testversion; sourceTree = BUILT_PRODUCTS_DIR; };
 		BEC567F50761D90600A33029 /* torturethread */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = torturethread; sourceTree = BUILT_PRODUCTS_DIR; };
-		DB0F48D717CA51D2008798C5 /* testdrawchessboard.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testdrawchessboard.c; path = ../../test/testdrawchessboard.c; sourceTree = "<group>"; };
-		DB0F48D817CA51D2008798C5 /* testfilesystem.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testfilesystem.c; path = ../../test/testfilesystem.c; sourceTree = "<group>"; };
+		DB0F48D717CA51D2008798C5 /* testdrawchessboard.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testdrawchessboard.c; sourceTree = "<group>"; };
+		DB0F48D817CA51D2008798C5 /* testfilesystem.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testfilesystem.c; sourceTree = "<group>"; };
 		DB0F48EC17CA51E5008798C5 /* testdrawchessboard */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testdrawchessboard; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB0F490117CA5212008798C5 /* testfilesystem */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testfilesystem; sourceTree = BUILT_PRODUCTS_DIR; };
-		DB166CBB16A1C74100A1396C /* testgesture.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testgesture.c; path = ../../test/testgesture.c; sourceTree = "<group>"; };
-		DB166CBC16A1C74100A1396C /* testgles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testgles.c; path = ../../test/testgles.c; sourceTree = "<group>"; };
-		DB166CBD16A1C74100A1396C /* testmessage.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testmessage.c; path = ../../test/testmessage.c; sourceTree = "<group>"; };
-		DB166CBF16A1C74100A1396C /* testrelative.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testrelative.c; path = ../../test/testrelative.c; sourceTree = "<group>"; };
-		DB166CC016A1C74100A1396C /* testrendercopyex.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testrendercopyex.c; path = ../../test/testrendercopyex.c; sourceTree = "<group>"; };
-		DB166CC116A1C74100A1396C /* testrendertarget.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testrendertarget.c; path = ../../test/testrendertarget.c; sourceTree = "<group>"; };
-		DB166CC216A1C74100A1396C /* testrumble.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testrumble.c; path = ../../test/testrumble.c; sourceTree = "<group>"; };
-		DB166CC316A1C74100A1396C /* testscale.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testscale.c; path = ../../test/testscale.c; sourceTree = "<group>"; };
-		DB166CC416A1C74100A1396C /* testshader.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testshader.c; path = ../../test/testshader.c; sourceTree = "<group>"; };
-		DB166CC516A1C74100A1396C /* testspriteminimal.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = testspriteminimal.c; path = ../../test/testspriteminimal.c; sourceTree = "<group>"; };
-		DB166CC616A1C74100A1396C /* teststreaming.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = teststreaming.c; path = ../../test/teststreaming.c; sourceTree = "<group>"; };
+		DB166CBB16A1C74100A1396C /* testgesture.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testgesture.c; sourceTree = "<group>"; };
+		DB166CBC16A1C74100A1396C /* testgles.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testgles.c; sourceTree = "<group>"; };
+		DB166CBD16A1C74100A1396C /* testmessage.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testmessage.c; sourceTree = "<group>"; };
+		DB166CBF16A1C74100A1396C /* testrelative.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testrelative.c; sourceTree = "<group>"; };
+		DB166CC016A1C74100A1396C /* testrendercopyex.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testrendercopyex.c; sourceTree = "<group>"; };
+		DB166CC116A1C74100A1396C /* testrendertarget.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testrendertarget.c; sourceTree = "<group>"; };
+		DB166CC216A1C74100A1396C /* testrumble.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testrumble.c; sourceTree = "<group>"; };
+		DB166CC316A1C74100A1396C /* testscale.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testscale.c; sourceTree = "<group>"; };
+		DB166CC416A1C74100A1396C /* testshader.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testshader.c; sourceTree = "<group>"; };
+		DB166CC516A1C74100A1396C /* testspriteminimal.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testspriteminimal.c; sourceTree = "<group>"; };
+		DB166CC616A1C74100A1396C /* teststreaming.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = teststreaming.c; sourceTree = "<group>"; };
 		DB166D7F16A1D12400A1396C /* libSDL_test.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDL_test.a; sourceTree = BUILT_PRODUCTS_DIR; };
-		DB166D8416A1D1A500A1396C /* SDL_test_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_assert.c; path = ../../src/test/SDL_test_assert.c; sourceTree = "<group>"; };
-		DB166D8516A1D1A500A1396C /* SDL_test_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_common.c; path = ../../src/test/SDL_test_common.c; sourceTree = "<group>"; };
-		DB166D8616A1D1A500A1396C /* SDL_test_compare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_compare.c; path = ../../src/test/SDL_test_compare.c; sourceTree = "<group>"; };
-		DB166D8716A1D1A500A1396C /* SDL_test_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_crc32.c; path = ../../src/test/SDL_test_crc32.c; sourceTree = "<group>"; };
-		DB166D8816A1D1A500A1396C /* SDL_test_font.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_font.c; path = ../../src/test/SDL_test_font.c; sourceTree = "<group>"; };
-		DB166D8916A1D1A500A1396C /* SDL_test_fuzzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_fuzzer.c; path = ../../src/test/SDL_test_fuzzer.c; sourceTree = "<group>"; };
-		DB166D8A16A1D1A500A1396C /* SDL_test_harness.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_harness.c; path = ../../src/test/SDL_test_harness.c; sourceTree = "<group>"; };
-		DB166D8B16A1D1A500A1396C /* SDL_test_imageBlit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imageBlit.c; path = ../../src/test/SDL_test_imageBlit.c; sourceTree = "<group>"; };
-		DB166D8C16A1D1A500A1396C /* SDL_test_imageBlitBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imageBlitBlend.c; path = ../../src/test/SDL_test_imageBlitBlend.c; sourceTree = "<group>"; };
-		DB166D8D16A1D1A500A1396C /* SDL_test_imageFace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imageFace.c; path = ../../src/test/SDL_test_imageFace.c; sourceTree = "<group>"; };
-		DB166D8E16A1D1A500A1396C /* SDL_test_imagePrimitives.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imagePrimitives.c; path = ../../src/test/SDL_test_imagePrimitives.c; sourceTree = "<group>"; };
-		DB166D8F16A1D1A500A1396C /* SDL_test_imagePrimitivesBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_imagePrimitivesBlend.c; path = ../../src/test/SDL_test_imagePrimitivesBlend.c; sourceTree = "<group>"; };
-		DB166D9016A1D1A500A1396C /* SDL_test_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_log.c; path = ../../src/test/SDL_test_log.c; sourceTree = "<group>"; };
-		DB166D9116A1D1A500A1396C /* SDL_test_md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_md5.c; path = ../../src/test/SDL_test_md5.c; sourceTree = "<group>"; };
-		DB166D9216A1D1A500A1396C /* SDL_test_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_test_random.c; path = ../../src/test/SDL_test_random.c; sourceTree = "<group>"; };
+		DB166D8416A1D1A500A1396C /* SDL_test_assert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_assert.c; sourceTree = "<group>"; };
+		DB166D8516A1D1A500A1396C /* SDL_test_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_common.c; sourceTree = "<group>"; };
+		DB166D8616A1D1A500A1396C /* SDL_test_compare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_compare.c; sourceTree = "<group>"; };
+		DB166D8716A1D1A500A1396C /* SDL_test_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_crc32.c; sourceTree = "<group>"; };
+		DB166D8816A1D1A500A1396C /* SDL_test_font.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_font.c; sourceTree = "<group>"; };
+		DB166D8916A1D1A500A1396C /* SDL_test_fuzzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_fuzzer.c; sourceTree = "<group>"; };
+		DB166D8A16A1D1A500A1396C /* SDL_test_harness.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_harness.c; sourceTree = "<group>"; };
+		DB166D8B16A1D1A500A1396C /* SDL_test_imageBlit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imageBlit.c; sourceTree = "<group>"; };
+		DB166D8C16A1D1A500A1396C /* SDL_test_imageBlitBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imageBlitBlend.c; sourceTree = "<group>"; };
+		DB166D8D16A1D1A500A1396C /* SDL_test_imageFace.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imageFace.c; sourceTree = "<group>"; };
+		DB166D8E16A1D1A500A1396C /* SDL_test_imagePrimitives.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imagePrimitives.c; sourceTree = "<group>"; };
+		DB166D8F16A1D1A500A1396C /* SDL_test_imagePrimitivesBlend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_imagePrimitivesBlend.c; sourceTree = "<group>"; };
+		DB166D9016A1D1A500A1396C /* SDL_test_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_log.c; sourceTree = "<group>"; };
+		DB166D9116A1D1A500A1396C /* SDL_test_md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_md5.c; sourceTree = "<group>"; };
+		DB166D9216A1D1A500A1396C /* SDL_test_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_test_random.c; sourceTree = "<group>"; };
 		DB166DBF16A1D2F600A1396C /* testgesture */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testgesture; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB166DD516A1D36A00A1396C /* testmessage */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testmessage; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB166DEE16A1D50C00A1396C /* testrelative */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testrelative; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1257,16 +1257,16 @@
 		DB166E6816A1D6F300A1396C /* testshader */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testshader; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB166E7E16A1D78400A1396C /* testspriteminimal */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testspriteminimal; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB166E9116A1D78C00A1396C /* teststreaming */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = teststreaming; sourceTree = BUILT_PRODUCTS_DIR; };
-		DB166ECF16A1D87000A1396C /* shapes */ = {isa = PBXFileReference; lastKnownFileType = folder; name = shapes; path = ../../test/shapes; sourceTree = "<group>"; };
+		DB166ECF16A1D87000A1396C /* shapes */ = {isa = PBXFileReference; lastKnownFileType = folder; path = shapes; sourceTree = "<group>"; };
 		DB445EF818184B7000B306B0 /* testdropfile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testdropfile.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		DB445EFA18184BB600B306B0 /* testdropfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testdropfile.c; path = ../../test/testdropfile.c; sourceTree = "<group>"; };
+		DB445EFA18184BB600B306B0 /* testdropfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testdropfile.c; sourceTree = "<group>"; };
 		DB89957E18A19ABA0092407C /* testhotplug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testhotplug; sourceTree = BUILT_PRODUCTS_DIR; };
-		DB89958318A19B130092407C /* testhotplug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testhotplug.c; path = ../../test/testhotplug.c; sourceTree = "<group>"; };
-		DBBC552C182831D700F3CA8D /* TestDropFile-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "TestDropFile-Info.plist"; sourceTree = "<group>"; };
-		DBEC54D11A1A811D005B1EAB /* controllermap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = controllermap.c; path = ../../test/controllermap.c; sourceTree = "<group>"; };
-		DBEC54D61A1A8145005B1EAB /* axis.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = axis.bmp; path = ../../test/axis.bmp; sourceTree = "<group>"; };
-		DBEC54D71A1A8145005B1EAB /* button.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = button.bmp; path = ../../test/button.bmp; sourceTree = "<group>"; };
-		DBEC54D81A1A8145005B1EAB /* controllermap.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = controllermap.bmp; path = ../../test/controllermap.bmp; sourceTree = "<group>"; };
+		DB89958318A19B130092407C /* testhotplug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testhotplug.c; sourceTree = "<group>"; };
+		DBBC552C182831D700F3CA8D /* TestDropFile-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestDropFile-Info.plist"; sourceTree = SOURCE_ROOT; };
+		DBEC54D11A1A811D005B1EAB /* controllermap.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = controllermap.c; sourceTree = "<group>"; };
+		DBEC54D61A1A8145005B1EAB /* axis.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = axis.bmp; sourceTree = "<group>"; };
+		DBEC54D71A1A8145005B1EAB /* button.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = button.bmp; sourceTree = "<group>"; };
+		DBEC54D81A1A8145005B1EAB /* controllermap.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; path = controllermap.bmp; sourceTree = "<group>"; };
 		DBEC54EA1A1A81C3005B1EAB /* controllermap */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = controllermap; sourceTree = BUILT_PRODUCTS_DIR; };
 		FA73672219A54A90004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = /System/Library/Frameworks/CoreVideo.framework; sourceTree = "<absolute>"; };
 /* End PBXFileReference section */
@@ -2165,6 +2165,7 @@
 				00794E6309D20839003FC8A1 /* utf8.txt */,
 			);
 			name = Resources;
+			path = ../../test;
 			sourceTree = "<group>";
 		};
 		08FB7794FE84155DC02AAC07 /* SDLTest */ = {
@@ -2238,6 +2239,7 @@
 				083E4887006D86A17F000001 /* torturethread.c */,
 			);
 			name = Source;
+			path = ../../test;
 			sourceTree = "<group>";
 		};
 		1AB674ADFE9D54B511CA2CBB /* Products */ = {
@@ -2323,6 +2325,7 @@
 				DB166D9216A1D1A500A1396C /* SDL_test_random.c */,
 			);
 			name = SDL_Test;
+			path = ../../src/test;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
@@ -4037,6 +4040,7 @@
 		002A85B21073008E007319AE /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(SRCROOT)/../SDL/build/$(CONFIGURATION)",
 					"$(HOME)/Library/Frameworks",
@@ -4163,6 +4167,7 @@
 		002A85D41073009D007319AE /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(SRCROOT)/../SDL/build/$(CONFIGURATION)",
 					"$(HOME)/Library/Frameworks",
diff --git a/source/acinclude/ax_gcc_x86_cpuid.m4.htm b/source/acinclude/ax_gcc_x86_cpuid.m4
similarity index 100%
rename from source/acinclude/ax_gcc_x86_cpuid.m4.htm
rename to source/acinclude/ax_gcc_x86_cpuid.m4
diff --git a/source/android-project/app/build.gradle b/source/android-project/app/build.gradle
index 2475c17..3900943 100644
--- a/source/android-project/app/build.gradle
+++ b/source/android-project/app/build.gradle
@@ -8,22 +8,21 @@
 }
 
 android {
-    compileSdkVersion 19
-    buildToolsVersion "26.0.1"
+    compileSdkVersion 26
     defaultConfig {
         if (buildAsApplication) {
             applicationId "org.libsdl.app"
         }
         minSdkVersion 14
-        targetSdkVersion 19
+        targetSdkVersion 26
         versionCode 1
         versionName "1.0"
         externalNativeBuild {
             ndkBuild {
                 arguments "APP_PLATFORM=android-14"
+                abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
             }
         }
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
     buildTypes {
         release {
@@ -60,9 +59,5 @@
 }
 
 dependencies {
-    compile fileTree(include: ['*.jar'], dir: 'libs')
-    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
-        exclude group: 'com.android.support', module: 'support-annotations'
-    })
-    testCompile 'junit:junit:4.12'
+    implementation fileTree(include: ['*.jar'], dir: 'libs')
 }
diff --git a/source/android-project/app/jni/Application.mk b/source/android-project/app/jni/Application.mk
index 246136d..15b2a73 100644
--- a/source/android-project/app/jni/Application.mk
+++ b/source/android-project/app/jni/Application.mk
@@ -1,7 +1,8 @@
 
 # Uncomment this if you're using STL in your project
-# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
-# APP_STL := stlport_static
+# You can find more information here:
+# https://developer.android.com/ndk/guides/cpp-support
+# APP_STL := c++_shared
 
 APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
 
diff --git a/source/android-project/app/src/main/AndroidManifest.xml b/source/android-project/app/src/main/AndroidManifest.xml
index 7e33072..25730cf 100644
--- a/source/android-project/app/src/main/AndroidManifest.xml
+++ b/source/android-project/app/src/main/AndroidManifest.xml
@@ -8,12 +8,24 @@
     android:versionName="1.0"
     android:installLocation="auto">
 
-    <!-- Android 4.0.1 -->
-    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="16" />
-
     <!-- OpenGL ES 2.0 -->
     <uses-feature android:glEsVersion="0x00020000" />
 
+    <!-- Touchscreen support -->
+    <uses-feature
+        android:name="android.hardware.touchscreen"
+        android:required="false" />
+
+    <!-- Game controller support -->
+    <uses-feature
+        android:name="android.hardware.gamepad"
+        android:required="false" />
+
+    <!-- External mouse input events -->
+    <uses-feature
+        android:name="android.hardware.type.pc"
+        android:required="false" />
+
     <!-- Allow writing to external storage -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <!-- Allow access to the vibrator -->
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java b/source/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java
new file mode 100644
index 0000000..aa358d1
--- /dev/null
+++ b/source/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java
@@ -0,0 +1,19 @@
+package org.libsdl.app;
+
+interface HIDDevice
+{
+    public int getId();
+    public int getVendorId();
+    public int getProductId();
+    public String getSerialNumber();
+    public int getVersion();
+    public String getManufacturerName();
+    public String getProductName();
+    public boolean open();
+    public int sendFeatureReport(byte[] report);
+    public int sendOutputReport(byte[] report);
+    public boolean getFeatureReport(byte[] report);
+    public void setFrozen(boolean frozen);
+    public void close();
+    public void shutdown();
+}
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
new file mode 100644
index 0000000..4cf114a
--- /dev/null
+++ b/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
@@ -0,0 +1,642 @@
+package org.libsdl.app;
+
+import android.content.Context;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothGattService;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+//import com.android.internal.util.HexDump;
+
+import java.lang.Runnable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.UUID;
+
+class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
+
+    private static final String TAG = "hidapi";
+    private HIDDeviceManager mManager;
+    private BluetoothDevice mDevice;
+    private int mDeviceId;
+    private BluetoothGatt mGatt;
+    private boolean mIsRegistered = false;
+    private boolean mIsConnected = false;
+    private boolean mIsChromebook = false;
+    private boolean mIsReconnecting = false;
+    private boolean mFrozen = false;
+    private LinkedList<GattOperation> mOperations;
+    GattOperation mCurrentOperation = null;
+    private Handler mHandler;
+
+    private static final int TRANSPORT_AUTO = 0;
+    private static final int TRANSPORT_BREDR = 1;
+    private static final int TRANSPORT_LE = 2;
+
+    private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
+
+    static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
+    static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
+    static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
+    static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
+
+    static class GattOperation {
+        private enum Operation {
+            CHR_READ,
+            CHR_WRITE,
+            ENABLE_NOTIFICATION
+        }
+
+        Operation mOp;
+        UUID mUuid;
+        byte[] mValue;
+        BluetoothGatt mGatt;
+        boolean mResult = true;
+
+        private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
+            mGatt = gatt;
+            mOp = operation;
+            mUuid = uuid;
+        }
+
+        private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
+            mGatt = gatt;
+            mOp = operation;
+            mUuid = uuid;
+            mValue = value;
+        }
+
+        public void run() {
+            // This is executed in main thread
+            BluetoothGattCharacteristic chr;
+
+            switch (mOp) {
+                case CHR_READ:
+                    chr = getCharacteristic(mUuid);
+                    //Log.v(TAG, "Reading characteristic " + chr.getUuid());
+                    if (!mGatt.readCharacteristic(chr)) {
+                        Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
+                        mResult = false;
+                        break;
+                    }
+                    mResult = true;
+                    break;
+                case CHR_WRITE:
+                    chr = getCharacteristic(mUuid);
+                    //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
+                    chr.setValue(mValue);
+                    if (!mGatt.writeCharacteristic(chr)) {
+                        Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
+                        mResult = false;
+                        break;
+                    }
+                    mResult = true;
+                    break;
+                case ENABLE_NOTIFICATION:
+                    chr = getCharacteristic(mUuid);
+                    //Log.v(TAG, "Writing descriptor of " + chr.getUuid());
+                    if (chr != null) {
+                        BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
+                        if (cccd != null) {
+                            int properties = chr.getProperties();
+                            byte[] value;
+                            if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
+                                value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
+                            } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
+                                value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
+                            } else {
+                                Log.e(TAG, "Unable to start notifications on input characteristic");
+                                mResult = false;
+                                return;
+                            }
+
+                            mGatt.setCharacteristicNotification(chr, true);
+                            cccd.setValue(value);
+                            if (!mGatt.writeDescriptor(cccd)) {
+                                Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
+                                mResult = false;
+                                return;
+                            }
+                            mResult = true;
+                        }
+                    }
+            }
+        }
+
+        public boolean finish() {
+            return mResult;
+        }
+
+        private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+            BluetoothGattService valveService = mGatt.getService(steamControllerService);
+            if (valveService == null)
+                return null;
+            return valveService.getCharacteristic(uuid);
+        }
+
+        static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
+            return new GattOperation(gatt, Operation.CHR_READ, uuid);
+        }
+
+        static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
+            return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
+        }
+
+        static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
+            return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
+        }
+    }
+
+    public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
+        mManager = manager;
+        mDevice = device;
+        mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
+        mIsRegistered = false;
+        mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+        mOperations = new LinkedList<GattOperation>();
+        mHandler = new Handler(Looper.getMainLooper());
+
+        mGatt = connectGatt();
+        final HIDDeviceBLESteamController finalThis = this;
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                finalThis.checkConnectionForChromebookIssue();
+            }
+        }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
+    }
+
+    public String getIdentifier() {
+        return String.format("SteamController.%s", mDevice.getAddress());
+    }
+
+    public BluetoothGatt getGatt() {
+        return mGatt;
+    }
+
+    // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
+    // of TRANSPORT_LE.  Let's force ourselves to connect low energy.
+    private BluetoothGatt connectGatt(boolean managed) {
+        try {
+            Method m = mDevice.getClass().getDeclaredMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class);
+            return (BluetoothGatt) m.invoke(mDevice, mManager.getContext(), managed, this, TRANSPORT_LE);
+        } catch (Exception e) {
+            return mDevice.connectGatt(mManager.getContext(), managed, this);
+        }
+    }
+
+    private BluetoothGatt connectGatt() {
+        return connectGatt(false);
+    }
+
+    protected int getConnectionState() {
+
+        Context context = mManager.getContext();
+        if (context == null) {
+            // We are lacking any context to get our Bluetooth information.  We'll just assume disconnected.
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
+        if (btManager == null) {
+            // This device doesn't support Bluetooth.  We should never be here, because how did
+            // we instantiate a device to start with?
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+
+        return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
+    }
+
+    public void reconnect() {
+
+        if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+            mGatt.disconnect();
+            mGatt = connectGatt();
+        }
+
+    }
+
+    protected void checkConnectionForChromebookIssue() {
+        if (!mIsChromebook) {
+            // We only do this on Chromebooks, because otherwise it's really annoying to just attempt
+            // over and over.
+            return;
+        }
+
+        int connectionState = getConnectionState();
+
+        switch (connectionState) {
+            case BluetoothProfile.STATE_CONNECTED:
+                if (!mIsConnected) {
+                    // We are in the Bad Chromebook Place.  We can force a disconnect
+                    // to try to recover.
+                    Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback.  Forcing a reconnect.");
+                    mIsReconnecting = true;
+                    mGatt.disconnect();
+                    mGatt = connectGatt(false);
+                    break;
+                }
+                else if (!isRegistered()) {
+                    if (mGatt.getServices().size() > 0) {
+                        Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration.  Trying to recover.");
+                        probeService(this);
+                    }
+                    else {
+                        Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services.  Trying to recover.");
+                        mIsReconnecting = true;
+                        mGatt.disconnect();
+                        mGatt = connectGatt(false);
+                        break;
+                    }
+                }
+                else {
+                    Log.v(TAG, "Chromebook: We are connected, and registered.  Everything's good!");
+                    return;
+                }
+                break;
+
+            case BluetoothProfile.STATE_DISCONNECTED:
+                Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us.  Attempting a disconnect/reconnect, but we may not be able to recover.");
+
+                mIsReconnecting = true;
+                mGatt.disconnect();
+                mGatt = connectGatt(false);
+                break;
+
+            case BluetoothProfile.STATE_CONNECTING:
+                Log.v(TAG, "Chromebook: We're still trying to connect.  Waiting a bit longer.");
+                break;
+        }
+
+        final HIDDeviceBLESteamController finalThis = this;
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                finalThis.checkConnectionForChromebookIssue();
+            }
+        }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
+    }
+
+    private boolean isRegistered() {
+        return mIsRegistered;
+    }
+
+    private void setRegistered() {
+        mIsRegistered = true;
+    }
+
+    private boolean probeService(HIDDeviceBLESteamController controller) {
+
+        if (isRegistered()) {
+            return true;
+        }
+
+        if (!mIsConnected) {
+            return false;
+        }
+
+        Log.v(TAG, "probeService controller=" + controller);
+
+        for (BluetoothGattService service : mGatt.getServices()) {
+            if (service.getUuid().equals(steamControllerService)) {
+                Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
+
+                for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
+                    if (chr.getUuid().equals(inputCharacteristic)) {
+                        Log.v(TAG, "Found input characteristic");
+                        // Start notifications
+                        BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
+                        if (cccd != null) {
+                            enableNotification(chr.getUuid());
+                        }
+                    }
+                }
+                return true;
+            }
+        }
+
+        if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
+            Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
+            mIsConnected = false;
+            mIsReconnecting = true;
+            mGatt.disconnect();
+            mGatt = connectGatt(false);
+        }
+
+        return false;
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private void finishCurrentGattOperation() {
+        GattOperation op = null;
+        synchronized (mOperations) {
+            if (mCurrentOperation != null) {
+                op = mCurrentOperation;
+                mCurrentOperation = null;
+            }
+        }
+        if (op != null) {
+            boolean result = op.finish(); // TODO: Maybe in main thread as well?
+
+            // Our operation failed, let's add it back to the beginning of our queue.
+            if (!result) {
+                mOperations.addFirst(op);
+            }
+        }
+        executeNextGattOperation();
+    }
+
+    private void executeNextGattOperation() {
+        synchronized (mOperations) {
+            if (mCurrentOperation != null)
+                return;
+
+            if (mOperations.isEmpty())
+                return;
+
+            mCurrentOperation = mOperations.removeFirst();
+        }
+
+        // Run in main thread
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mOperations) {
+                    if (mCurrentOperation == null) {
+                        Log.e(TAG, "Current operation null in executor?");
+                        return;
+                    }
+
+                    mCurrentOperation.run();
+                    // now wait for the GATT callback and when it comes, finish this operation
+                }
+            }
+        });
+    }
+
+    private void queueGattOperation(GattOperation op) {
+        synchronized (mOperations) {
+            mOperations.add(op);
+        }
+        executeNextGattOperation();
+    }
+
+    private void enableNotification(UUID chrUuid) {
+        GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
+        queueGattOperation(op);
+    }
+
+    public void writeCharacteristic(UUID uuid, byte[] value) {
+        GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
+        queueGattOperation(op);
+    }
+
+    public void readCharacteristic(UUID uuid) {
+        GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
+        queueGattOperation(op);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////  BluetoothGattCallback overridden methods
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
+        //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
+        mIsReconnecting = false;
+        if (newState == 2) {
+            mIsConnected = true;
+            // Run directly, without GattOperation
+            if (!isRegistered()) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mGatt.discoverServices();
+                    }
+                });
+            }
+        } 
+        else if (newState == 0) {
+            mIsConnected = false;
+        }
+
+        // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
+    }
+
+    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+        //Log.v(TAG, "onServicesDiscovered status=" + status);
+        if (status == 0) {
+            if (gatt.getServices().size() == 0) {
+                Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
+                mIsReconnecting = true;
+                mIsConnected = false;
+                gatt.disconnect();
+                mGatt = connectGatt(false);
+            }
+            else {
+                probeService(this);
+            }
+        }
+    }
+
+    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+        //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
+
+        if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
+            mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
+        }
+
+        finishCurrentGattOperation();
+    }
+
+    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+        //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
+
+        if (characteristic.getUuid().equals(reportCharacteristic)) {
+            // Only register controller with the native side once it has been fully configured
+            if (!isRegistered()) {
+                Log.v(TAG, "Registering Steam Controller with ID: " + getId());
+                mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0);
+                setRegistered();
+            }
+        }
+
+        finishCurrentGattOperation();
+    }
+
+    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+    // Enable this for verbose logging of controller input reports
+        //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
+
+        if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
+            mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
+        }
+    }
+
+    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+        //Log.v(TAG, "onDescriptorRead status=" + status);
+    }
+
+    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+        BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
+        //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
+
+        if (chr.getUuid().equals(inputCharacteristic)) {
+            boolean hasWrittenInputDescriptor = true;
+            BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
+            if (reportChr != null) {
+                Log.v(TAG, "Writing report characteristic to enter valve mode");
+                reportChr.setValue(enterValveMode);
+                gatt.writeCharacteristic(reportChr);
+            }
+        }
+
+        finishCurrentGattOperation();
+    }
+
+    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+        //Log.v(TAG, "onReliableWriteCompleted status=" + status);
+    }
+
+    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+        //Log.v(TAG, "onReadRemoteRssi status=" + status);
+    }
+
+    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+        //Log.v(TAG, "onMtuChanged status=" + status);
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////// Public API
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public int getId() {
+        return mDeviceId;
+    }
+
+    @Override
+    public int getVendorId() {
+        // Valve Corporation
+        final int VALVE_USB_VID = 0x28DE;
+        return VALVE_USB_VID;
+    }
+
+    @Override
+    public int getProductId() {
+        // We don't have an easy way to query from the Bluetooth device, but we know what it is
+        final int D0G_BLE2_PID = 0x1106;
+        return D0G_BLE2_PID;
+    }
+
+    @Override
+    public String getSerialNumber() {
+        // This will be read later via feature report by Steam
+        return "12345";
+    }
+
+    @Override
+    public int getVersion() {
+        return 0;
+    }
+
+    @Override
+    public String getManufacturerName() {
+        return "Valve Corporation";
+    }
+
+    @Override
+    public String getProductName() {
+        return "Steam Controller";
+    }
+
+    @Override
+    public boolean open() {
+        return true;
+    }
+
+    @Override
+    public int sendFeatureReport(byte[] report) {
+        if (!isRegistered()) {
+            Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
+            if (mIsConnected) {
+                probeService(this);
+            }
+            return -1;
+        }
+
+        // We need to skip the first byte, as that doesn't go over the air
+        byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
+        //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
+        writeCharacteristic(reportCharacteristic, actual_report);
+        return report.length;
+    }
+
+    @Override
+    public int sendOutputReport(byte[] report) {
+        if (!isRegistered()) {
+            Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
+            if (mIsConnected) {
+                probeService(this);
+            }
+            return -1;
+        }
+
+        //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
+        writeCharacteristic(reportCharacteristic, report);
+        return report.length;
+    }
+
+    @Override
+    public boolean getFeatureReport(byte[] report) {
+        if (!isRegistered()) {
+            Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
+            if (mIsConnected) {
+                probeService(this);
+            }
+            return false;
+        }
+
+        //Log.v(TAG, "getFeatureReport");
+        readCharacteristic(reportCharacteristic);
+        return true;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public void setFrozen(boolean frozen) {
+        mFrozen = frozen;
+    }
+
+    @Override
+    public void shutdown() {
+        close();
+
+        BluetoothGatt g = mGatt;
+        if (g != null) {
+            g.disconnect();
+            g.close();
+            mGatt = null;
+        }
+        mManager = null;
+        mIsRegistered = false;
+        mIsConnected = false;
+        mOperations.clear();
+    }
+
+}
+
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
new file mode 100644
index 0000000..db9400f
--- /dev/null
+++ b/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
@@ -0,0 +1,682 @@
+package org.libsdl.app;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.util.Log;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.hardware.usb.*;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HIDDeviceManager {
+    private static final String TAG = "hidapi";
+    private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
+
+    private static HIDDeviceManager sManager;
+    private static int sManagerRefCount = 0;
+
+    public static HIDDeviceManager acquire(Context context) {
+        if (sManagerRefCount == 0) {
+            sManager = new HIDDeviceManager(context);
+        }
+        ++sManagerRefCount;
+        return sManager;
+    }
+
+    public static void release(HIDDeviceManager manager) {
+        if (manager == sManager) {
+            --sManagerRefCount;
+            if (sManagerRefCount == 0) {
+                sManager.close();
+                sManager = null;
+            }
+        }
+    }
+
+    private Context mContext;
+    private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
+    private HashMap<UsbDevice, HIDDeviceUSB> mUSBDevices = new HashMap<UsbDevice, HIDDeviceUSB>();
+    private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
+    private int mNextDeviceId = 0;
+    private SharedPreferences mSharedPreferences = null;
+    private boolean mIsChromebook = false;
+    private UsbManager mUsbManager;
+    private Handler mHandler;
+    private BluetoothManager mBluetoothManager;
+    private List<BluetoothDevice> mLastBluetoothDevices;
+
+    private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceAttached(usbDevice);
+            } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
+                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDeviceDetached(usbDevice);
+            } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
+                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
+            }
+        }
+    };
+
+    private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            // Bluetooth device was connected. If it was a Steam Controller, handle it
+            if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                Log.d(TAG, "Bluetooth device connected: " + device);
+
+                if (isSteamController(device)) {
+                    connectBluetoothDevice(device);
+                }
+            }
+
+            // Bluetooth device was disconnected, remove from controller manager (if any)
+            if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                Log.d(TAG, "Bluetooth device disconnected: " + device);
+
+                disconnectBluetoothDevice(device);
+            }
+        }
+    };
+
+    private HIDDeviceManager(final Context context) {
+        mContext = context;
+
+        // Make sure we have the HIDAPI library loaded with the native functions
+        try {
+            SDL.loadLibrary("hidapi");
+        } catch (Throwable e) {
+            Log.w(TAG, "Couldn't load hidapi: " + e.toString());
+
+            AlertDialog.Builder builder = new AlertDialog.Builder(context);
+            builder.setCancelable(false);
+            builder.setTitle("SDL HIDAPI Error");
+            builder.setMessage("Please report the following error to the SDL maintainers: " + e.getMessage());
+            builder.setNegativeButton("Quit", new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    try {
+                        // If our context is an activity, exit rather than crashing when we can't
+                        // call our native functions.
+                        Activity activity = (Activity)context;
+        
+                        activity.finish();
+                    }
+                    catch (ClassCastException cce) {
+                        // Context wasn't an activity, there's nothing we can do.  Give up and return.
+                    }
+                }
+            });
+            builder.show();
+
+            return;
+        }
+        
+        HIDDeviceRegisterCallback();
+
+        mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
+        mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+
+//        if (shouldClear) {
+//            SharedPreferences.Editor spedit = mSharedPreferences.edit();
+//            spedit.clear();
+//            spedit.commit();
+//        }
+//        else
+        {
+            mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
+        }
+
+        initializeUSB();
+        initializeBluetooth();
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public int getDeviceIDForIdentifier(String identifier) {
+        SharedPreferences.Editor spedit = mSharedPreferences.edit();
+
+        int result = mSharedPreferences.getInt(identifier, 0);
+        if (result == 0) {
+            result = mNextDeviceId++;
+            spedit.putInt("next_device_id", mNextDeviceId);
+        }
+
+        spedit.putInt(identifier, result);
+        spedit.commit();
+        return result;
+    }
+
+    private void initializeUSB() {
+        mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
+
+        /*
+        // Logging
+        for (UsbDevice device : mUsbManager.getDeviceList().values()) {
+            Log.i(TAG,"Path: " + device.getDeviceName());
+            Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
+            Log.i(TAG,"Product: " + device.getProductName());
+            Log.i(TAG,"ID: " + device.getDeviceId());
+            Log.i(TAG,"Class: " + device.getDeviceClass());
+            Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
+            Log.i(TAG,"Vendor ID " + device.getVendorId());
+            Log.i(TAG,"Product ID: " + device.getProductId());
+            Log.i(TAG,"Interface count: " + device.getInterfaceCount());
+            Log.i(TAG,"---------------------------------------");
+
+            // Get interface details
+            for (int index = 0; index < device.getInterfaceCount(); index++) {
+                UsbInterface mUsbInterface = device.getInterface(index);
+                Log.i(TAG,"  *****     *****");
+                Log.i(TAG,"  Interface index: " + index);
+                Log.i(TAG,"  Interface ID: " + mUsbInterface.getId());
+                Log.i(TAG,"  Interface class: " + mUsbInterface.getInterfaceClass());
+                Log.i(TAG,"  Interface subclass: " + mUsbInterface.getInterfaceSubclass());
+                Log.i(TAG,"  Interface protocol: " + mUsbInterface.getInterfaceProtocol());
+                Log.i(TAG,"  Endpoint count: " + mUsbInterface.getEndpointCount());
+
+                // Get endpoint details 
+                for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
+                {
+                    UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
+                    Log.i(TAG,"    ++++   ++++   ++++");
+                    Log.i(TAG,"    Endpoint index: " + epi);
+                    Log.i(TAG,"    Attributes: " + mEndpoint.getAttributes());
+                    Log.i(TAG,"    Direction: " + mEndpoint.getDirection());
+                    Log.i(TAG,"    Number: " + mEndpoint.getEndpointNumber());
+                    Log.i(TAG,"    Interval: " + mEndpoint.getInterval());
+                    Log.i(TAG,"    Packet size: " + mEndpoint.getMaxPacketSize());
+                    Log.i(TAG,"    Type: " + mEndpoint.getType());
+                }
+            }
+        }
+        Log.i(TAG," No more devices connected.");
+        */
+
+        // Register for USB broadcasts and permission completions
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
+        mContext.registerReceiver(mUsbBroadcast, filter);
+
+        for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
+            handleUsbDeviceAttached(usbDevice);
+        }
+    }
+
+    UsbManager getUSBManager() {
+        return mUsbManager;
+    }
+
+    private void shutdownUSB() {
+        try {
+            mContext.unregisterReceiver(mUsbBroadcast);
+        } catch (Exception e) {
+            // We may not have registered, that's okay
+        }
+    }
+
+    private boolean isHIDDeviceUSB(UsbDevice usbDevice) {
+        for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) {
+            if (isHIDDeviceInterface(usbDevice, interface_number)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) {
+        UsbInterface usbInterface = usbDevice.getInterface(interface_number);
+        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
+            return true;
+        }
+        if (interface_number == 0) {
+            if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
+        final int XB360_IFACE_SUBCLASS = 93;
+        final int XB360_IFACE_PROTOCOL = 1; // Wired only
+        final int[] SUPPORTED_VENDORS = {
+            0x0079, // GPD Win 2
+            0x044f, // Thrustmaster
+            0x045e, // Microsoft
+            0x046d, // Logitech
+            0x056e, // Elecom
+            0x06a3, // Saitek
+            0x0738, // Mad Catz
+            0x07ff, // Mad Catz
+            0x0e6f, // Unknown
+            0x0f0d, // Hori
+            0x11c9, // Nacon
+            0x12ab, // Unknown
+            0x1430, // RedOctane
+            0x146b, // BigBen
+            0x1532, // Razer Sabertooth
+            0x15e4, // Numark
+            0x162e, // Joytech
+            0x1689, // Razer Onza
+            0x1bad, // Harmonix
+            0x24c6, // PowerA
+        };
+
+        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
+            usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
+            usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
+            int vendor_id = usbDevice.getVendorId();
+            for (int supportedVid : SUPPORTED_VENDORS) {
+                if (vendor_id == supportedVid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
+        final int XB1_IFACE_SUBCLASS = 71;
+        final int XB1_IFACE_PROTOCOL = 208;
+        final int[] SUPPORTED_VENDORS = {
+            0x045e, // Microsoft
+            0x0738, // Mad Catz
+            0x0e6f, // Unknown
+            0x0f0d, // Hori
+            0x1532, // Razer Wildcat
+            0x24c6, // PowerA
+        };
+
+        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
+            usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
+            usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
+            int vendor_id = usbDevice.getVendorId();
+            for (int supportedVid : SUPPORTED_VENDORS) {
+                if (vendor_id == supportedVid) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void handleUsbDeviceAttached(UsbDevice usbDevice) {
+        if (isHIDDeviceUSB(usbDevice)) {
+            connectHIDDeviceUSB(usbDevice);
+        }
+    }
+
+    private void handleUsbDeviceDetached(UsbDevice usbDevice) {
+        HIDDeviceUSB device = mUSBDevices.get(usbDevice);
+        if (device == null)
+            return;
+
+        int id = device.getId();
+        mUSBDevices.remove(usbDevice);
+        mDevicesById.remove(id);
+        device.shutdown();
+        HIDDeviceDisconnected(id);
+    }
+
+    private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
+        HIDDeviceUSB device = mUSBDevices.get(usbDevice);
+        if (device == null)
+            return;
+
+        boolean opened = false;
+        if (permission_granted) {
+            opened = device.open();
+        }
+        HIDDeviceOpenResult(device.getId(), opened);
+    }
+
+    private void connectHIDDeviceUSB(UsbDevice usbDevice) {
+        synchronized (this) {
+            for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) {
+                if (isHIDDeviceInterface(usbDevice, interface_number)) {
+                    HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_number);
+                    int id = device.getId();
+                    mUSBDevices.put(usbDevice, device);
+                    mDevicesById.put(id, device);
+                    HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number);
+                    break;
+                }
+            }
+        }
+    }
+
+    private void initializeBluetooth() {
+        Log.d(TAG, "Initializing Bluetooth");
+
+        if (mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+            Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
+            return;
+        }
+
+        // Find bonded bluetooth controllers and create SteamControllers for them
+        mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+        if (mBluetoothManager == null) {
+            // This device doesn't support Bluetooth.
+            return;
+        }
+
+        BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
+        if (btAdapter == null) {
+            // This device has Bluetooth support in the codebase, but has no available adapters.
+            return;
+        }
+
+        // Get our bonded devices.
+        for (BluetoothDevice device : btAdapter.getBondedDevices()) {
+
+            Log.d(TAG, "Bluetooth device available: " + device);
+            if (isSteamController(device)) {
+                connectBluetoothDevice(device);
+            }
+
+        }
+
+        // NOTE: These don't work on Chromebooks, to my undying dismay.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mContext.registerReceiver(mBluetoothBroadcast, filter);
+
+        if (mIsChromebook) {
+            mHandler = new Handler(Looper.getMainLooper());
+            mLastBluetoothDevices = new ArrayList<>();
+
+            // final HIDDeviceManager finalThis = this;
+            // mHandler.postDelayed(new Runnable() {
+            //     @Override
+            //     public void run() {
+            //         finalThis.chromebookConnectionHandler();
+            //     }
+            // }, 5000);
+        }
+    }
+
+    private void shutdownBluetooth() {
+        try {
+            mContext.unregisterReceiver(mBluetoothBroadcast);
+        } catch (Exception e) {
+            // We may not have registered, that's okay
+        }
+    }
+
+    // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
+    // This function provides a sort of dummy version of that, watching for changes in the
+    // connected devices and attempting to add controllers as things change.
+    public void chromebookConnectionHandler() {
+        if (!mIsChromebook) {
+            return;
+        }
+
+        ArrayList<BluetoothDevice> disconnected = new ArrayList<>();
+        ArrayList<BluetoothDevice> connected = new ArrayList<>();
+
+        List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
+
+        for (BluetoothDevice bluetoothDevice : currentConnected) {
+            if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
+                connected.add(bluetoothDevice);
+            }
+        }
+        for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
+            if (!currentConnected.contains(bluetoothDevice)) {
+                disconnected.add(bluetoothDevice);
+            }
+        }
+
+        mLastBluetoothDevices = currentConnected;
+
+        for (BluetoothDevice bluetoothDevice : disconnected) {
+            disconnectBluetoothDevice(bluetoothDevice);
+        }
+        for (BluetoothDevice bluetoothDevice : connected) {
+            connectBluetoothDevice(bluetoothDevice);
+        }
+
+        final HIDDeviceManager finalThis = this;
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                finalThis.chromebookConnectionHandler();
+            }
+        }, 10000);
+    }
+
+    public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
+        synchronized (this) {
+            if (mBluetoothDevices.containsKey(bluetoothDevice)) {
+                Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
+
+                HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
+                device.reconnect();
+
+                return false;
+            }
+            HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
+            int id = device.getId();
+            mBluetoothDevices.put(bluetoothDevice, device);
+            mDevicesById.put(id, device);
+
+            // The Steam Controller will mark itself connected once initialization is complete
+        }
+        return true;
+    }
+
+    public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        synchronized (this) {
+            HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
+            if (device == null)
+                return;
+
+            int id = device.getId();
+            mBluetoothDevices.remove(bluetoothDevice);
+            mDevicesById.remove(id);
+            device.shutdown();
+            HIDDeviceDisconnected(id);
+        }
+    }
+
+    public boolean isSteamController(BluetoothDevice bluetoothDevice) {
+        // Sanity check.  If you pass in a null device, by definition it is never a Steam Controller.
+        if (bluetoothDevice == null) {
+            return false;
+        }
+
+        // If the device has no local name, we really don't want to try an equality check against it.
+        if (bluetoothDevice.getName() == null) {
+            return false;
+        }
+
+        return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
+    }
+
+    private void close() {
+        shutdownUSB();
+        shutdownBluetooth();
+        synchronized (this) {
+            for (HIDDevice device : mDevicesById.values()) {
+                device.shutdown();
+            }
+            mDevicesById.clear();
+            mBluetoothDevices.clear();
+            HIDDeviceReleaseCallback();
+        }
+    }
+
+    public void setFrozen(boolean frozen) {
+        synchronized (this) {
+            for (HIDDevice device : mDevicesById.values()) {
+                device.setFrozen(frozen);
+            }
+        }        
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private HIDDevice getDevice(int id) {
+        synchronized (this) {
+            HIDDevice result = mDevicesById.get(id);
+            if (result == null) {
+                Log.v(TAG, "No device for id: " + id);
+                Log.v(TAG, "Available devices: " + mDevicesById.keySet());
+            }
+            return result;
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    ////////// JNI interface functions
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    public boolean openDevice(int deviceID) {
+        // Look to see if this is a USB device and we have permission to access it
+        for (HIDDeviceUSB device : mUSBDevices.values()) {
+            if (deviceID == device.getId()) {
+                UsbDevice usbDevice = device.getDevice();
+                if (!mUsbManager.hasPermission(usbDevice)) {
+                    HIDDeviceOpenPending(deviceID);
+                    try {
+                        mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0));
+                    } catch (Exception e) {
+                        Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
+                        HIDDeviceOpenResult(deviceID, false);
+                    }
+                    return false;
+                }
+                break;
+            }
+        }
+
+        try {
+            Log.v(TAG, "openDevice deviceID=" + deviceID);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return false;
+            }
+
+            return device.open();
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return false;
+    }
+
+    public int sendOutputReport(int deviceID, byte[] report) {
+        try {
+            Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return -1;
+            }
+
+            return device.sendOutputReport(report);
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return -1;
+    }
+
+    public int sendFeatureReport(int deviceID, byte[] report) {
+        try {
+            Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return -1;
+            }
+
+            return device.sendFeatureReport(report);
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return -1;
+    }
+
+    public boolean getFeatureReport(int deviceID, byte[] report) {
+        try {
+            Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return false;
+            }
+
+            return device.getFeatureReport(report);
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+        return false;
+    }
+
+    public void closeDevice(int deviceID) {
+        try {
+            Log.v(TAG, "closeDevice deviceID=" + deviceID);
+            HIDDevice device;
+            device = getDevice(deviceID);
+            if (device == null) {
+                HIDDeviceDisconnected(deviceID);
+                return;
+            }
+
+            device.close();
+        } catch (Exception e) {
+            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+        }
+    }
+
+
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+    /////////////// Native methods
+    //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+    private native void HIDDeviceRegisterCallback();
+    private native void HIDDeviceReleaseCallback();
+
+    native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number);
+    native void HIDDeviceOpenPending(int deviceID);
+    native void HIDDeviceOpenResult(int deviceID, boolean opened);
+    native void HIDDeviceDisconnected(int deviceID);
+
+    native void HIDDeviceInputReport(int deviceID, byte[] report);
+    native void HIDDeviceFeatureReport(int deviceID, byte[] report);
+}
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
new file mode 100644
index 0000000..c9fc58e
--- /dev/null
+++ b/source/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
@@ -0,0 +1,307 @@
+package org.libsdl.app;
+
+import android.hardware.usb.*;
+import android.os.Build;
+import android.util.Log;
+import java.util.Arrays;
+
+class HIDDeviceUSB implements HIDDevice {
+
+    private static final String TAG = "hidapi";
+
+    protected HIDDeviceManager mManager;
+    protected UsbDevice mDevice;
+    protected int mInterface;
+    protected int mDeviceId;
+    protected UsbDeviceConnection mConnection;
+    protected UsbEndpoint mInputEndpoint;
+    protected UsbEndpoint mOutputEndpoint;
+    protected InputThread mInputThread;
+    protected boolean mRunning;
+    protected boolean mFrozen;
+
+    public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_number) {
+        mManager = manager;
+        mDevice = usbDevice;
+        mInterface = interface_number;
+        mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
+        mRunning = false;
+    }
+
+    public String getIdentifier() {
+        return String.format("%s/%x/%x", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId());
+    }
+
+    @Override
+    public int getId() {
+        return mDeviceId;
+    }
+
+    @Override
+    public int getVendorId() {
+        return mDevice.getVendorId();
+    }
+
+    @Override
+    public int getProductId() {
+        return mDevice.getProductId();
+    }
+
+    @Override
+    public String getSerialNumber() {
+        String result = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            result = mDevice.getSerialNumber();
+        }
+        if (result == null) {
+            result = "";
+        }
+        return result;
+    }
+
+    @Override
+    public int getVersion() {
+        return 0;
+    }
+
+    @Override
+    public String getManufacturerName() {
+        String result = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            result = mDevice.getManufacturerName();
+        }
+        if (result == null) {
+            result = String.format("%x", getVendorId());
+        }
+        return result;
+    }
+
+    @Override
+    public String getProductName() {
+        String result = null;
+        if (Build.VERSION.SDK_INT >= 21) {
+            result = mDevice.getProductName();
+        }
+        if (result == null) {
+            result = String.format("%x", getProductId());
+        }
+        return result;
+    }
+
+    public UsbDevice getDevice() {
+        return mDevice;
+    }
+
+    public String getDeviceName() {
+        return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
+    }
+
+    @Override
+    public boolean open() {
+        mConnection = mManager.getUSBManager().openDevice(mDevice);
+        if (mConnection == null) {
+            Log.w(TAG, "Unable to open USB device " + getDeviceName());
+            return false;
+        }
+
+        // Force claim all interfaces
+        for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+            UsbInterface iface = mDevice.getInterface(i);
+
+            if (!mConnection.claimInterface(iface, true)) {
+                Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
+                close();
+                return false;
+            }
+        }
+
+        // Find the endpoints
+        UsbInterface iface = mDevice.getInterface(mInterface);
+        for (int j = 0; j < iface.getEndpointCount(); j++) {
+            UsbEndpoint endpt = iface.getEndpoint(j);
+            switch (endpt.getDirection()) {
+            case UsbConstants.USB_DIR_IN:
+                if (mInputEndpoint == null) {
+                    mInputEndpoint = endpt;
+                }
+                break;
+            case UsbConstants.USB_DIR_OUT:
+                if (mOutputEndpoint == null) {
+                    mOutputEndpoint = endpt;
+                }
+                break;
+            }
+        }
+
+        // Make sure the required endpoints were present
+        if (mInputEndpoint == null || mOutputEndpoint == null) {
+            Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
+            close();
+            return false;
+        }
+
+        // Start listening for input
+        mRunning = true;
+        mInputThread = new InputThread();
+        mInputThread.start();
+
+        return true;
+    }
+
+    @Override
+    public int sendFeatureReport(byte[] report) {
+        int res = -1;
+        int offset = 0;
+        int length = report.length;
+        boolean skipped_report_id = false;
+        byte report_number = report[0];
+
+        if (report_number == 0x0) {
+            ++offset;
+            --length;
+            skipped_report_id = true;
+        }
+
+        res = mConnection.controlTransfer(
+            UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
+            0x09/*HID set_report*/,
+            (3/*HID feature*/ << 8) | report_number,
+            0,
+            report, offset, length,
+            1000/*timeout millis*/);
+
+        if (res < 0) {
+            Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
+            return -1;
+        }
+
+        if (skipped_report_id) {
+            ++length;
+        }
+        return length;
+    }
+
+    @Override
+    public int sendOutputReport(byte[] report) {
+        int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
+        if (r != report.length) {
+            Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
+        }
+        return r;
+    }
+
+    @Override
+    public boolean getFeatureReport(byte[] report) {
+        int res = -1;
+        int offset = 0;
+        int length = report.length;
+        boolean skipped_report_id = false;
+        byte report_number = report[0];
+
+        if (report_number == 0x0) {
+            /* Offset the return buffer by 1, so that the report ID
+               will remain in byte 0. */
+            ++offset;
+            --length;
+            skipped_report_id = true;
+        }
+
+        res = mConnection.controlTransfer(
+            UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
+            0x01/*HID get_report*/,
+            (3/*HID feature*/ << 8) | report_number,
+            0,
+            report, offset, length,
+            1000/*timeout millis*/);
+
+        if (res < 0) {
+            Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
+            return false;
+        }
+
+        if (skipped_report_id) {
+            ++res;
+            ++length;
+        }
+
+        byte[] data;
+        if (res == length) {
+            data = report;
+        } else {
+            data = Arrays.copyOfRange(report, 0, res);
+        }
+        mManager.HIDDeviceFeatureReport(mDeviceId, data);
+
+        return true;
+    }
+
+    @Override
+    public void close() {
+        mRunning = false;
+        if (mInputThread != null) {
+            while (mInputThread.isAlive()) {
+                mInputThread.interrupt();
+                try {
+                    mInputThread.join();
+                } catch (InterruptedException e) {
+                    // Keep trying until we're done
+                }
+            }
+            mInputThread = null;
+        }
+        if (mConnection != null) {
+            for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+                UsbInterface iface = mDevice.getInterface(i);
+                mConnection.releaseInterface(iface);
+            }
+            mConnection.close();
+            mConnection = null;
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        close();
+        mManager = null;
+    }
+
+    @Override
+    public void setFrozen(boolean frozen) {
+        mFrozen = frozen;
+    }
+
+    protected class InputThread extends Thread {
+        @Override
+        public void run() {
+            int packetSize = mInputEndpoint.getMaxPacketSize();
+            byte[] packet = new byte[packetSize];
+            while (mRunning) {
+                int r;
+                try
+                {
+                    r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
+                }
+                catch (Exception e)
+                {
+                    Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
+                    break;
+                }
+                if (r < 0) {
+                    // Could be a timeout or an I/O error
+                }
+                if (r > 0) {
+                    byte[] data;
+                    if (r == packetSize) {
+                        data = packet;
+                    } else {
+                        data = Arrays.copyOfRange(packet, 0, r);
+                    }
+
+                    if (!mFrozen) {
+                        mManager.HIDDeviceInputReport(mDeviceId, data);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/SDL.java b/source/android-project/app/src/main/java/org/libsdl/app/SDL.java
index cfe4830..fb7f731 100644
--- a/source/android-project/app/src/main/java/org/libsdl/app/SDL.java
+++ b/source/android-project/app/src/main/java/org/libsdl/app/SDL.java
@@ -2,6 +2,8 @@
 
 import android.content.Context;
 
+import java.lang.reflect.*;
+
 /**
     SDL library initialization
 */
@@ -33,5 +35,50 @@
         return mContext;
     }
 
+    public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
+
+        if (libraryName == null) {
+            throw new NullPointerException("No library name provided.");
+        }
+
+        try {
+            // Let's see if we have ReLinker available in the project.  This is necessary for 
+            // some projects that have huge numbers of local libraries bundled, and thus may 
+            // trip a bug in Android's native library loader which ReLinker works around.  (If
+            // loadLibrary works properly, ReLinker will simply use the normal Android method
+            // internally.)
+            //
+            // To use ReLinker, just add it as a dependency.  For more information, see 
+            // https://github.com/KeepSafe/ReLinker for ReLinker's repository.
+            //
+            Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
+            Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
+            Class contextClass = mContext.getClassLoader().loadClass("android.content.Context");
+            Class stringClass = mContext.getClassLoader().loadClass("java.lang.String");
+
+            // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if 
+            // they've changed during updates.
+            Method forceMethod = relinkClass.getDeclaredMethod("force");
+            Object relinkInstance = forceMethod.invoke(null);
+            Class relinkInstanceClass = relinkInstance.getClass();
+
+            // Actually load the library!
+            Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
+            loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
+        }
+        catch (final Throwable e) {
+            // Fall back
+            try {
+                System.loadLibrary(libraryName);
+            }
+            catch (final UnsatisfiedLinkError ule) {
+                throw ule;
+            }
+            catch (final SecurityException se) {
+                throw se;
+            }
+        }        
+    }
+
     protected static Context mContext;
 }
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/source/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index 8c363ed..311b2f1 100644
--- a/source/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/source/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -3,7 +3,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
+import java.util.Hashtable;
 import java.lang.reflect.Method;
+import java.lang.Math;
 
 import android.app.*;
 import android.content.*;
@@ -32,10 +34,33 @@
 /**
     SDL Activity
 */
-public class SDLActivity extends Activity {
+public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
     private static final String TAG = "SDL";
 
     public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus;
+
+    // Cursor types
+    private static final int SDL_SYSTEM_CURSOR_NONE = -1;
+    private static final int SDL_SYSTEM_CURSOR_ARROW = 0;
+    private static final int SDL_SYSTEM_CURSOR_IBEAM = 1;
+    private static final int SDL_SYSTEM_CURSOR_WAIT = 2;
+    private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3;
+    private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4;
+    private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5;
+    private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6;
+    private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7;
+    private static final int SDL_SYSTEM_CURSOR_SIZENS = 8;
+    private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9;
+    private static final int SDL_SYSTEM_CURSOR_NO = 10;
+    private static final int SDL_SYSTEM_CURSOR_HAND = 11;
+
+    protected static final int SDL_ORIENTATION_UNKNOWN = 0;
+    protected static final int SDL_ORIENTATION_LANDSCAPE = 1;
+    protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2;
+    protected static final int SDL_ORIENTATION_PORTRAIT = 3;
+    protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4;
+
+    protected static int mCurrentOrientation;
 
     // Handle the state of the native layer
     public enum NativeState {
@@ -61,10 +86,28 @@
     protected static boolean mScreenKeyboardShown;
     protected static ViewGroup mLayout;
     protected static SDLClipboardHandler mClipboardHandler;
-
+    protected static Hashtable<Integer, Object> mCursors;
+    protected static int mLastCursorID;
+    protected static SDLGenericMotionListener_API12 mMotionListener;
+    protected static HIDDeviceManager mHIDDeviceManager;
 
     // This is what SDL runs in. It invokes SDL_main(), eventually
     protected static Thread mSDLThread;
+
+    protected static SDLGenericMotionListener_API12 getMotionListener() {
+        if (mMotionListener == null) {
+            if (Build.VERSION.SDK_INT >= 26) {
+                mMotionListener = new SDLGenericMotionListener_API26();
+            } else 
+            if (Build.VERSION.SDK_INT >= 24) {
+                mMotionListener = new SDLGenericMotionListener_API24();
+            } else {
+                mMotionListener = new SDLGenericMotionListener_API12();
+            }
+        }
+
+        return mMotionListener;
+    }
 
     /**
      * This method returns the name of the shared object with the application entry point
@@ -78,7 +121,7 @@
         } else {
             library = "libmain.so";
         }
-        return library;
+        return getContext().getApplicationInfo().nativeLibraryDir + "/" + library;
     }
 
     /**
@@ -111,7 +154,7 @@
     // Load the .so
     public void loadLibraries() {
        for (String lib : getLibraries()) {
-          System.loadLibrary(lib);
+          SDL.loadLibrary(lib);
        }
     }
 
@@ -133,6 +176,8 @@
         mTextEdit = null;
         mLayout = null;
         mClipboardHandler = null;
+        mCursors = new Hashtable<Integer, Object>();
+        mLastCursorID = 0;
         mSDLThread = null;
         mExitCalledFromJava = false;
         mBrokenLibraries = false;
@@ -146,8 +191,8 @@
     // Setup
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        Log.v(TAG, "Device: " + android.os.Build.DEVICE);
-        Log.v(TAG, "Model: " + android.os.Build.MODEL);
+        Log.v(TAG, "Device: " + Build.DEVICE);
+        Log.v(TAG, "Model: " + Build.MODEL);
         Log.v(TAG, "onCreate()");
         super.onCreate(savedInstanceState);
 
@@ -205,15 +250,23 @@
             mClipboardHandler = new SDLClipboardHandler_Old();
         }
 
+        mHIDDeviceManager = HIDDeviceManager.acquire(this);
+
         // Set up the surface
         mSurface = new SDLSurface(getApplication());
 
         mLayout = new RelativeLayout(this);
         mLayout.addView(mSurface);
 
+        // Get our current screen orientation and pass it down.
+        mCurrentOrientation = SDLActivity.getCurrentOrientation();
+        SDLActivity.onNativeOrientationChanged(mCurrentOrientation);
+
         setContentView(mLayout);
 
         setWindowStyle(false);
+
+        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
 
         // Get filename from "Open with" of another application
         Intent intent = getIntent();
@@ -238,6 +291,10 @@
            return;
         }
 
+        if (mHIDDeviceManager != null) {
+            mHIDDeviceManager.setFrozen(true);
+        }
+
         SDLActivity.handleNativeState();
     }
 
@@ -252,9 +309,39 @@
            return;
         }
 
+        if (mHIDDeviceManager != null) {
+            mHIDDeviceManager.setFrozen(false);
+        }
+
         SDLActivity.handleNativeState();
     }
 
+    public static int getCurrentOrientation() {
+        final Context context = SDLActivity.getContext();
+        final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+
+        int result = SDL_ORIENTATION_UNKNOWN;
+
+        switch (display.getRotation()) {
+            case Surface.ROTATION_0:
+                result = SDL_ORIENTATION_PORTRAIT;
+                break;
+    
+            case Surface.ROTATION_90:
+                result = SDL_ORIENTATION_LANDSCAPE;
+                break;
+    
+            case Surface.ROTATION_180:
+                result = SDL_ORIENTATION_PORTRAIT_FLIPPED;
+                break;
+    
+            case Surface.ROTATION_270:
+                result = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
+                break;
+        }
+
+        return result;
+    }
 
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
@@ -268,6 +355,7 @@
         SDLActivity.mHasFocus = hasFocus;
         if (hasFocus) {
            mNextNativeState = NativeState.RESUMED;
+           SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded();
         } else {
            mNextNativeState = NativeState.PAUSED;
         }
@@ -290,6 +378,11 @@
     @Override
     protected void onDestroy() {
         Log.v(TAG, "onDestroy()");
+
+        if (mHIDDeviceManager != null) {
+            HIDDeviceManager.release(mHIDDeviceManager);
+            mHIDDeviceManager = null;
+        }
 
         if (SDLActivity.mBrokenLibraries) {
            super.onDestroy();
@@ -321,6 +414,43 @@
 
         // Reset everything in case the user re opens the app
         SDLActivity.initialize();
+    }
+
+    @Override
+    public void onBackPressed() {
+        // Check if we want to block the back button in case of mouse right click.
+        //
+        // If we do, the normal hardware back button will no longer work and people have to use home,
+        // but the mouse right click will work.
+        //
+        String trapBack = SDLActivity.nativeGetHint("SDL_ANDROID_TRAP_BACK_BUTTON");
+        if ((trapBack != null) && trapBack.equals("1")) {
+            // Exit and let the mouse handler handle this button (if appropriate)
+            return;
+        }
+
+        // Default system back button behavior.
+        super.onBackPressed();
+    }
+
+    // Called by JNI from SDL.
+    public static void manualBackButton() {
+        mSingleton.pressBackButton();
+    }
+
+    // Used to get us onto the activity's main thread
+    public void pressBackButton() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                SDLActivity.this.superOnBackPressed();
+            }
+        });
+    }
+
+    // Used to access the system back behavior.
+    public void superOnBackPressed() {
+        super.onBackPressed();
     }
 
     @Override
@@ -390,7 +520,9 @@
     /* The native thread has finished */
     public static void handleNativeExit() {
         SDLActivity.mSDLThread = null;
-        mSingleton.finish();
+        if (mSingleton != null) {
+            mSingleton.finish();
+        }
     }
 
 
@@ -401,6 +533,8 @@
     static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
 
     protected static final int COMMAND_USER = 0x8000;
+
+    protected static boolean mFullscreenModeActive;
 
     /**
      * This method is called by SDL if SDL did not handle a message itself.
@@ -440,30 +574,31 @@
                     // This version of Android doesn't support the immersive fullscreen mode
                     break;
                 }
-/* This needs more testing, per bug 4096 - Enabling fullscreen on Android causes the app to toggle fullscreen mode continuously in a loop
- ***
                 if (context instanceof Activity) {
                     Window window = ((Activity) context).getWindow();
                     if (window != null) {
                         if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
-                            int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
-                                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
-                                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
                                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
-                                        View.SYSTEM_UI_FLAG_FULLSCREEN |
-                                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+                                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
+                                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+                                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
                             window.getDecorView().setSystemUiVisibility(flags);        
                             window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                            window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+                            SDLActivity.mFullscreenModeActive = true;
                         } else {
-                            int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
-                            window.getDecorView().setSystemUiVisibility(flags);        
+                            int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE;
+                            window.getDecorView().setSystemUiVisibility(flags);
+                            window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                             window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                            SDLActivity.mFullscreenModeActive = false;
                         }
                     }
                 } else {
                     Log.e(TAG, "error handling message, getContext() returned no Activity");
                 }
-***/
                 break;
             case COMMAND_TEXTEDIT_HIDE:
                 if (mTextEdit != null) {
@@ -508,7 +643,61 @@
         Message msg = commandHandler.obtainMessage();
         msg.arg1 = command;
         msg.obj = data;
-        return commandHandler.sendMessage(msg);
+        boolean result = commandHandler.sendMessage(msg);
+
+        if ((Build.VERSION.SDK_INT >= 19) && (command == COMMAND_CHANGE_WINDOW_STYLE)) {
+            // Ensure we don't return until the resize has actually happened,
+            // or 500ms have passed.
+
+            boolean bShouldWait = false;
+            
+            if (data instanceof Integer) {
+                // Let's figure out if we're already laid out fullscreen or not.
+                Display display = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+                android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics();
+                display.getRealMetrics( realMetrics );
+        
+                boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) && 
+                                             (realMetrics.heightPixels == mSurface.getHeight()));
+
+                if (((Integer)data).intValue() == 1) {
+                    // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going
+                    // to change size and should wait for surfaceChanged() before we return, so the size
+                    // is right back in native code.  If we're already laid out fullscreen, though, we're
+                    // not going to change size even if we change decor modes, so we shouldn't wait for
+                    // surfaceChanged() -- which may not even happen -- and should return immediately.
+                    bShouldWait = !bFullscreenLayout;
+                }
+                else {
+                    // If we're laid out fullscreen (even if the status bar and nav bar are present),
+                    // or are actively in fullscreen, we're going to change size and should wait for
+                    // surfaceChanged before we return, so the size is right back in native code.
+                    bShouldWait = bFullscreenLayout;
+                }
+            }
+
+            if (bShouldWait) {
+                // We'll wait for the surfaceChanged() method, which will notify us
+                // when called.  That way, we know our current size is really the
+                // size we need, instead of grabbing a size that's still got
+                // the navigation and/or status bars before they're hidden.
+                //
+                // We'll wait for up to half a second, because some devices 
+                // take a surprisingly long time for the surface resize, but
+                // then we'll just give up and return.
+                //
+                synchronized(SDLActivity.getContext()) {
+                    try {
+                        SDLActivity.getContext().wait(500);
+                    }
+                    catch (InterruptedException ie) {
+                        ie.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        return result;
     }
 
     // C functions we call
@@ -519,11 +708,11 @@
     public static native void nativePause();
     public static native void nativeResume();
     public static native void onNativeDropFile(String filename);
-    public static native void onNativeResize(int x, int y, int format, float rate);
+    public static native void onNativeResize(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, int format, float rate);
     public static native void onNativeKeyDown(int keycode);
     public static native void onNativeKeyUp(int keycode);
     public static native void onNativeKeyboardFocusLost();
-    public static native void onNativeMouse(int button, int action, float x, float y);
+    public static native void onNativeMouse(int button, int action, float x, float y, boolean relative);
     public static native void onNativeTouch(int touchDevId, int pointerFingerId,
                                             int action, float x,
                                             float y, float p);
@@ -533,6 +722,7 @@
     public static native void onNativeSurfaceDestroyed();
     public static native String nativeGetHint(String name);
     public static native void nativeSetenv(String name, String value);
+    public static native void onNativeOrientationChanged(int orientation);
 
     /**
      * This method is called by SDL using JNI.
@@ -602,7 +792,6 @@
         }
     }
 
-
     /**
      * This method is called by SDL using JNI.
      */
@@ -619,6 +808,42 @@
         InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
         return imm.isAcceptingText();
 
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean supportsRelativeMouse()
+    {
+        // ChromeOS doesn't provide relative mouse motion via the Android 7 APIs
+        if (isChromebook()) {
+            return false;
+        }
+
+        // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under 
+        // Android 7 APIs, and simply returns no data under Android 8 APIs.
+        //
+        // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and
+        // thus SDK version 27.  If we are in DeX mode and not API 27 or higher, as a result,
+        // we should stick to relative mode.
+        //
+        if ((Build.VERSION.SDK_INT < 27) && isDeXMode()) {
+            return false;
+        }
+
+        return SDLActivity.getMotionListener().supportsRelativeMouse();
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setRelativeMouseEnabled(boolean enabled)
+    {
+        if (enabled && !supportsRelativeMouse()) {
+            return false;
+        }
+
+        return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled);
     }
 
     /**
@@ -643,7 +868,57 @@
      */
     public static boolean isAndroidTV() {
         UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE);
-        return (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION);
+        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+            return true;
+        }
+        if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) {
+            return true;
+        }
+        if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isTablet() {
+        DisplayMetrics metrics = new DisplayMetrics();
+        Activity activity = (Activity)getContext();
+        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+        double dWidthInches = metrics.widthPixels / (double)metrics.xdpi;
+        double dHeightInches = metrics.heightPixels / (double)metrics.ydpi;
+
+        double dDiagonal = Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches));
+
+        // If our diagonal size is seven inches or greater, we consider ourselves a tablet.
+        return (dDiagonal >= 7.0);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isChromebook() {
+        return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+    }    
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean isDeXMode() {
+        if (Build.VERSION.SDK_INT < 24) {
+            return false;
+        }
+        try {
+            final Configuration config = getContext().getResources().getConfiguration();
+            final Class configClass = config.getClass();
+            return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
+                    == configClass.getField("semDesktopModeEnabled").getInt(config);
+        } catch(Exception ignored) {
+            return false;
+        }
     }
 
     /**
@@ -678,6 +953,12 @@
            Log.v("SDL", "exception " + e.toString());
         }
         return false;
+    }
+
+    // This method is called by SDLControllerManager's API 26 Generic Motion Handler.
+    public static View getContentView() 
+    {
+        return mSingleton.mLayout;
     }
 
     static class ShowTextInputTask implements Runnable {
@@ -1045,13 +1326,39 @@
         return dialog;
     }
 
+    private final Runnable rehideSystemUi = new Runnable() {
+        @Override
+        public void run() {
+            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
+                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
+                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+                        View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
+
+            SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags);
+        }
+    };
+
+    public void onSystemUiVisibilityChange(int visibility) {
+        if (SDLActivity.mFullscreenModeActive && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
+
+            Handler handler = getWindow().getDecorView().getHandler();
+            if (handler != null) {
+                handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop.
+                handler.postDelayed(rehideSystemUi, 2000);
+            }
+
+        }
+    }    
+
     /**
      * This method is called by SDL using JNI.
      */
     public static boolean clipboardHasText() {
         return mClipboardHandler.clipboardHasText();
     }
-    
+
     /**
      * This method is called by SDL using JNI.
      */
@@ -1064,6 +1371,95 @@
      */
     public static void clipboardSetText(String string) {
         mClipboardHandler.clipboardSetText(string);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) {
+        Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
+        ++mLastCursorID;
+        // This requires API 24, so use reflection to implement this
+        try {
+            Class PointerIconClass = Class.forName("android.view.PointerIcon");
+            Class[] arg_types = new Class[] { Bitmap.class, float.class, float.class };
+            Method create = PointerIconClass.getMethod("create", arg_types);
+            mCursors.put(mLastCursorID, create.invoke(null, bitmap, hotSpotX, hotSpotY));
+        } catch (Exception e) {
+            return 0;
+        }
+        return mLastCursorID;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setCustomCursor(int cursorID) {
+        // This requires API 24, so use reflection to implement this
+        try {
+            Class PointerIconClass = Class.forName("android.view.PointerIcon");
+            Method setPointerIcon = SDLSurface.class.getMethod("setPointerIcon", PointerIconClass);
+            setPointerIcon.invoke(mSurface, mCursors.get(cursorID));
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setSystemCursor(int cursorID) {
+        int cursor_type = 0; //PointerIcon.TYPE_NULL;
+        switch (cursorID) {
+        case SDL_SYSTEM_CURSOR_ARROW:
+            cursor_type = 1000; //PointerIcon.TYPE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_IBEAM:
+            cursor_type = 1008; //PointerIcon.TYPE_TEXT;
+            break;
+        case SDL_SYSTEM_CURSOR_WAIT:
+            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
+            break;
+        case SDL_SYSTEM_CURSOR_CROSSHAIR:
+            cursor_type = 1007; //PointerIcon.TYPE_CROSSHAIR;
+            break;
+        case SDL_SYSTEM_CURSOR_WAITARROW:
+            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENWSE:
+            cursor_type = 1017; //PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENESW:
+            cursor_type = 1016; //PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZEWE:
+            cursor_type = 1014; //PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZENS:
+            cursor_type = 1015; //PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+            break;
+        case SDL_SYSTEM_CURSOR_SIZEALL:
+            cursor_type = 1020; //PointerIcon.TYPE_GRAB;
+            break;
+        case SDL_SYSTEM_CURSOR_NO:
+            cursor_type = 1012; //PointerIcon.TYPE_NO_DROP;
+            break;
+        case SDL_SYSTEM_CURSOR_HAND:
+            cursor_type = 1002; //PointerIcon.TYPE_HAND;
+            break;
+        }
+        // This requires API 24, so use reflection to implement this
+        try {
+            Class PointerIconClass = Class.forName("android.view.PointerIcon");
+            Class[] arg_types = new Class[] { Context.class, int.class };
+            Method getSystemIcon = PointerIconClass.getMethod("getSystemIcon", arg_types);
+            Method setPointerIcon = SDLSurface.class.getMethod("setPointerIcon", PointerIconClass);
+            setPointerIcon.invoke(mSurface, getSystemIcon.invoke(null, SDL.getContext(), cursor_type));
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
     }
 }
 
@@ -1122,7 +1518,7 @@
         mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
 
         if (Build.VERSION.SDK_INT >= 12) {
-            setOnGenericMotionListener(new SDLGenericMotionListener_API12());
+            setOnGenericMotionListener(SDLActivity.getMotionListener());
         }
 
         // Some arbitrary defaults to avoid a potential division by zero
@@ -1173,6 +1569,10 @@
                                int format, int width, int height) {
         Log.v("SDL", "surfaceChanged()");
 
+        if (SDLActivity.mSingleton == null) {
+            return;
+        }
+
         int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
         switch (format) {
         case PixelFormat.A_8:
@@ -1220,9 +1620,27 @@
 
         mWidth = width;
         mHeight = height;
-        SDLActivity.onNativeResize(width, height, sdlFormat, mDisplay.getRefreshRate());
-        Log.v("SDL", "Window size: " + width + "x" + height);
+        int nDeviceWidth = width;
+        int nDeviceHeight = height;
+        try
+        {
+            if (Build.VERSION.SDK_INT >= 17) {
+                android.util.DisplayMetrics realMetrics = new android.util.DisplayMetrics();
+                mDisplay.getRealMetrics( realMetrics );
+                nDeviceWidth = realMetrics.widthPixels;
+                nDeviceHeight = realMetrics.heightPixels;
+            }
+        }
+        catch ( java.lang.Throwable throwable ) {}
 
+        synchronized(SDLActivity.getContext()) {
+            // In case we're waiting on a size change after going fullscreen, send a notification.
+            SDLActivity.getContext().notifyAll();
+        }
+
+        Log.v("SDL", "Window size: " + width + "x" + height);
+        Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
+        SDLActivity.onNativeResize(width, height, nDeviceWidth, nDeviceHeight, sdlFormat, mDisplay.getRefreshRate());
 
         boolean skip = false;
         int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
@@ -1337,7 +1755,8 @@
         float x,y,p;
 
         // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
-        if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
+        // 12290 = Samsung DeX mode desktop mouse
+        if ((event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == 12290) && SDLActivity.mSeparateMouseAndTouch) {
             if (Build.VERSION.SDK_INT < 14) {
                 mouseButton = 1; // all mouse buttons are the left button
             } else {
@@ -1347,7 +1766,14 @@
                     mouseButton = 1;    // oh well.
                 }
             }
-            SDLActivity.onNativeMouse(mouseButton, action, event.getX(0), event.getY(0));
+
+            // We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
+            // if we are.  We'll leverage our existing mouse motion listener
+            SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
+            x = motionListener.getEventX(event);
+            y = motionListener.getEventY(event);
+
+            SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
         } else {
             switch(action) {
                 case MotionEvent.ACTION_MOVE:
@@ -1432,30 +1858,90 @@
     @Override
     public void onSensorChanged(SensorEvent event) {
         if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+
+            // Since we may have an orientation set, we won't receive onConfigurationChanged events.
+            // We thus should check here.
+            int newOrientation = SDLActivity.SDL_ORIENTATION_UNKNOWN;
+    
             float x, y;
             switch (mDisplay.getRotation()) {
                 case Surface.ROTATION_90:
                     x = -event.values[1];
                     y = event.values[0];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
                     break;
                 case Surface.ROTATION_270:
                     x = event.values[1];
                     y = -event.values[0];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
                     break;
                 case Surface.ROTATION_180:
                     x = -event.values[1];
                     y = -event.values[0];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
                     break;
                 default:
                     x = event.values[0];
                     y = event.values[1];
+                    newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
                     break;
             }
+
+            if (newOrientation != SDLActivity.mCurrentOrientation) {
+                SDLActivity.mCurrentOrientation = newOrientation;
+                SDLActivity.onNativeOrientationChanged(newOrientation);
+            }
+
             SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
                                       y / SensorManager.GRAVITY_EARTH,
                                       event.values[2] / SensorManager.GRAVITY_EARTH);
+
+            
         }
     }
+
+    // Captured pointer events for API 26.
+    public boolean onCapturedPointerEvent(MotionEvent event)
+    {
+        int action = event.getActionMasked();
+
+        float x, y;
+        switch (action) {
+            case MotionEvent.ACTION_SCROLL:
+                x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                SDLActivity.onNativeMouse(0, action, x, y, false);
+                return true;
+
+            case MotionEvent.ACTION_HOVER_MOVE:
+            case MotionEvent.ACTION_MOVE:
+                x = event.getX(0);
+                y = event.getY(0);
+                SDLActivity.onNativeMouse(0, action, x, y, true);
+                return true;
+
+            case MotionEvent.ACTION_BUTTON_PRESS:
+            case MotionEvent.ACTION_BUTTON_RELEASE:
+
+                // Change our action value to what SDL's code expects.
+                if (action == MotionEvent.ACTION_BUTTON_PRESS) {
+                    action = MotionEvent.ACTION_DOWN;
+                }
+                else if (action == MotionEvent.ACTION_BUTTON_RELEASE) {
+                    action = MotionEvent.ACTION_UP;
+                }
+
+                x = event.getX(0);
+                y = event.getY(0);
+                int button = event.getButtonState();
+
+                SDLActivity.onNativeMouse(button, action, x, y, true);
+                return true;
+        }
+
+        return false;
+    }
+
 }
 
 /* This is a fake invisible editor view that receives the input and defines the
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java b/source/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
index 26baf82..bed0eb5 100644
--- a/source/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
+++ b/source/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java
@@ -1,6 +1,7 @@
 package org.libsdl.app;
 
 import android.media.*;
+import android.os.Build;
 import android.util.Log;
 
 public class SDLAudioManager
@@ -17,41 +18,250 @@
 
     // Audio
 
-    /**
-     * This method is called by SDL using JNI.
-     */
-    public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
-        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
-        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
-        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+    protected static String getAudioFormatString(int audioFormat) {
+        switch (audioFormat) {
+        case AudioFormat.ENCODING_PCM_8BIT:
+            return "8-bit";
+        case AudioFormat.ENCODING_PCM_16BIT:
+            return "16-bit";
+        case AudioFormat.ENCODING_PCM_FLOAT:
+            return "float";
+        default:
+            return Integer.toString(audioFormat);
+        }
+    }
 
-        Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+    protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+        int channelConfig;
+        int sampleSize;
+        int frameSize;
+
+        Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
+
+        /* On older devices let's use known good settings */
+        if (Build.VERSION.SDK_INT < 21) {
+            if (desiredChannels > 2) {
+                desiredChannels = 2;
+            }
+            if (sampleRate < 8000) {
+                sampleRate = 8000;
+            } else if (sampleRate > 48000) {
+                sampleRate = 48000;
+            }
+        }
+
+        if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
+            int minSDKVersion = (isCapture ? 23 : 21);
+            if (Build.VERSION.SDK_INT < minSDKVersion) {
+                audioFormat = AudioFormat.ENCODING_PCM_16BIT;
+            }
+        }
+        switch (audioFormat)
+        {
+        case AudioFormat.ENCODING_PCM_8BIT:
+            sampleSize = 1;
+            break;
+        case AudioFormat.ENCODING_PCM_16BIT:
+            sampleSize = 2;
+            break;
+        case AudioFormat.ENCODING_PCM_FLOAT:
+            sampleSize = 4;
+            break;
+        default:
+            Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
+            audioFormat = AudioFormat.ENCODING_PCM_16BIT;
+            sampleSize = 2;
+            break;
+        }
+ 
+        if (isCapture) {
+            switch (desiredChannels) {
+            case 1:
+                channelConfig = AudioFormat.CHANNEL_IN_MONO;
+                break;
+            case 2:
+                channelConfig = AudioFormat.CHANNEL_IN_STEREO;
+                break;
+            default:
+                Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
+                desiredChannels = 2;
+                channelConfig = AudioFormat.CHANNEL_IN_STEREO;
+                break;
+            }
+        } else {
+            switch (desiredChannels) {
+            case 1:
+                channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+                break;
+            case 2:
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+                break;
+            case 3:
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
+                break;
+            case 4:
+                channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
+                break;
+            case 5:
+                channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
+                break;
+            case 6:
+                channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
+                break;
+            case 7:
+                channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
+                break;
+            case 8:
+                if (Build.VERSION.SDK_INT >= 23) {
+                    channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
+                } else {
+                    Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
+                    desiredChannels = 6;
+                    channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
+                }
+                break;
+            default:
+                Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
+                desiredChannels = 2;
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+                break;
+            }
+
+/*
+            Log.v(TAG, "Speaker configuration (and order of channels):");
+
+            if ((channelConfig & 0x00000004) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_LEFT");
+            }
+            if ((channelConfig & 0x00000008) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_RIGHT");
+            }
+            if ((channelConfig & 0x00000010) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_CENTER");
+            }
+            if ((channelConfig & 0x00000020) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_LOW_FREQUENCY");
+            }
+            if ((channelConfig & 0x00000040) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_BACK_LEFT");
+            }
+            if ((channelConfig & 0x00000080) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_BACK_RIGHT");
+            }
+            if ((channelConfig & 0x00000100) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
+            }
+            if ((channelConfig & 0x00000200) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
+            }
+            if ((channelConfig & 0x00000400) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_BACK_CENTER");
+            }
+            if ((channelConfig & 0x00000800) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_SIDE_LEFT");
+            }
+            if ((channelConfig & 0x00001000) != 0) {
+                Log.v(TAG, "   CHANNEL_OUT_SIDE_RIGHT");
+            }
+*/
+        }
+        frameSize = (sampleSize * desiredChannels);
 
         // Let the user pick a larger buffer if they really want -- but ye
         // gods they probably shouldn't, the minimums are horrifyingly high
         // latency already
-        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+        int minBufferSize;
+        if (isCapture) {
+            minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
+        } else {
+            minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
+        }
+        desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
 
-        if (mAudioTrack == null) {
-            mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
-                    channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+        int[] results = new int[4];
 
-            // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
-            // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
-            // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
+        if (isCapture) {
+            if (mAudioRecord == null) {
+                mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
+                        channelConfig, audioFormat, desiredFrames * frameSize);
 
-            if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
-                Log.e(TAG, "Failed during initialization of Audio Track");
-                mAudioTrack = null;
-                return -1;
+                // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
+                if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                    Log.e(TAG, "Failed during initialization of AudioRecord");
+                    mAudioRecord.release();
+                    mAudioRecord = null;
+                    return null;
+                }
+
+                mAudioRecord.startRecording();
             }
 
-            mAudioTrack.play();
+            results[0] = mAudioRecord.getSampleRate();
+            results[1] = mAudioRecord.getAudioFormat();
+            results[2] = mAudioRecord.getChannelCount();
+            results[3] = desiredFrames;
+
+        } else {
+            if (mAudioTrack == null) {
+                mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+
+                // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
+                // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
+                // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
+                if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+                    /* Try again, with safer values */
+
+                    Log.e(TAG, "Failed during initialization of Audio Track");
+                    mAudioTrack.release();
+                    mAudioTrack = null;
+                    return null;
+                }
+
+                mAudioTrack.play();
+            }
+
+            results[0] = mAudioTrack.getSampleRate();
+            results[1] = mAudioTrack.getAudioFormat();
+            results[2] = mAudioTrack.getChannelCount();
+            results[3] = desiredFrames;
         }
 
-        Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+        Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
 
-        return 0;
+        return results;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+        return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void audioWriteFloatBuffer(float[] buffer) {
+        if (mAudioTrack == null) {
+            Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
+            return;
+        }
+
+        for (int i = 0; i < buffer.length;) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(1);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w(TAG, "SDL audio: error return from write(float)");
+                return;
+            }
+        }
     }
 
     /**
@@ -63,7 +273,7 @@
             return;
         }
 
-        for (int i = 0; i < buffer.length; ) {
+        for (int i = 0; i < buffer.length;) {
             int result = mAudioTrack.write(buffer, i, buffer.length - i);
             if (result > 0) {
                 i += result;
@@ -109,52 +319,32 @@
     /**
      * This method is called by SDL using JNI.
      */
-    public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
-        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
-        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
-        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+    public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
+        return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
+    }
 
-        Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
-        // Let the user pick a larger buffer if they really want -- but ye
-        // gods they probably shouldn't, the minimums are horrifyingly high
-        // latency already
-        desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
-
-        if (mAudioRecord == null) {
-            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
-                    channelConfig, audioFormat, desiredFrames * frameSize);
-
-            // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
-            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
-                Log.e(TAG, "Failed during initialization of AudioRecord");
-                mAudioRecord.release();
-                mAudioRecord = null;
-                return -1;
-            }
-
-            mAudioRecord.startRecording();
-        }
-
-        Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
-
-        return 0;
+    /** This method is called by SDL using JNI. */
+    public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
+        return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
     }
 
     /** This method is called by SDL using JNI. */
     public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
-        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
-        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
-        return mAudioRecord.read(buffer, 0, buffer.length);
+        if (Build.VERSION.SDK_INT < 23) {
+            return mAudioRecord.read(buffer, 0, buffer.length);
+        } else {
+            return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        }
     }
 
     /** This method is called by SDL using JNI. */
     public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
-        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
-        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
-        return mAudioRecord.read(buffer, 0, buffer.length);
+        if (Build.VERSION.SDK_INT < 23) {
+            return mAudioRecord.read(buffer, 0, buffer.length);
+        } else {
+            return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        }
     }
-
 
     /** This method is called by SDL using JNI. */
     public static void audioClose() {
diff --git a/source/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/source/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
index 7b82c0e..6c5623d 100644
--- a/source/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
+++ b/source/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java
@@ -11,13 +11,14 @@
 import android.util.Log;
 
 
-public class SDLControllerManager 
+public class SDLControllerManager
 {
 
     public static native int nativeSetupJNI();
 
     public static native int nativeAddJoystick(int device_id, String name, String desc,
-                                               int is_accelerometer, int nbuttons,
+                                               int vendor_id, int product_id,
+                                               boolean is_accelerometer, int button_mask,
                                                int naxes, int nhats, int nballs);
     public static native int nativeRemoveJoystick(int device_id);
     public static native int nativeAddHaptic(int device_id, String name);
@@ -35,21 +36,25 @@
     private static final String TAG = "SDLControllerManager";
 
     public static void initialize() {
-        mJoystickHandler = null;
-        mHapticHandler = null;
-
-        SDLControllerManager.setup();
-    }
-
-    public static void setup() {
-        if (Build.VERSION.SDK_INT >= 16) {
-            mJoystickHandler = new SDLJoystickHandler_API16();
-        } else if (Build.VERSION.SDK_INT >= 12) {
-            mJoystickHandler = new SDLJoystickHandler_API12();
-        } else {
-            mJoystickHandler = new SDLJoystickHandler();
+        if (mJoystickHandler == null) {
+            if (Build.VERSION.SDK_INT >= 19) {
+                mJoystickHandler = new SDLJoystickHandler_API19();
+            } else if (Build.VERSION.SDK_INT >= 16) {
+                mJoystickHandler = new SDLJoystickHandler_API16();
+            } else if (Build.VERSION.SDK_INT >= 12) {
+                mJoystickHandler = new SDLJoystickHandler_API12();
+            } else {
+                mJoystickHandler = new SDLJoystickHandler();
+            }
         }
-        mHapticHandler = new SDLHapticHandler();
+
+        if (mHapticHandler == null) {
+            if (Build.VERSION.SDK_INT >= 26) {
+                mHapticHandler = new SDLHapticHandler_API26();
+            } else {
+                mHapticHandler = new SDLHapticHandler();
+            }
+        }
     }
 
     // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
@@ -74,8 +79,16 @@
     /**
      * This method is called by SDL using JNI.
      */
-    public static void hapticRun(int device_id, int length) {
-        mHapticHandler.run(device_id, length);
+    public static void hapticRun(int device_id, float intensity, int length) {
+        mHapticHandler.run(device_id, intensity, length);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void hapticStop(int device_id)
+    {
+        mHapticHandler.stop(device_id);
     }
 
     // Check if a given device is considered a possible SDL joystick
@@ -141,7 +154,21 @@
     static class RangeComparator implements Comparator<InputDevice.MotionRange> {
         @Override
         public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
-            return arg0.getAxis() - arg1.getAxis();
+            // Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
+            int arg0Axis = arg0.getAxis();
+            int arg1Axis = arg1.getAxis();
+            if (arg0Axis == MotionEvent.AXIS_GAS) {
+                arg0Axis = MotionEvent.AXIS_BRAKE;
+            } else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
+                arg0Axis = MotionEvent.AXIS_GAS;
+            }
+            if (arg1Axis == MotionEvent.AXIS_GAS) {
+                arg1Axis = MotionEvent.AXIS_BRAKE;
+            } else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
+                arg1Axis = MotionEvent.AXIS_GAS;
+            }
+
+            return arg0Axis - arg1Axis;
         }
     }
 
@@ -155,12 +182,7 @@
     @Override
     public void pollInputDevices() {
         int[] deviceIds = InputDevice.getDeviceIds();
-        // It helps processing the device ids in reverse order
-        // For example, in the case of the XBox 360 wireless dongle,
-        // so the first controller seen by SDL matches what the receiver
-        // considers to be the first controller
-
-        for(int i=deviceIds.length-1; i>-1; i--) {
+        for(int i=0; i < deviceIds.length; ++i) {
             SDLJoystick joystick = getJoystick(deviceIds[i]);
             if (joystick == null) {
                 joystick = new SDLJoystick();
@@ -187,8 +209,7 @@
                     }
 
                     mJoysticks.add(joystick);
-                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
-                                                           joystick.axes.size(), joystick.hats.size()/2, 0);
+                    SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, getVendorId(joystickDevice), getProductId(joystickDevice), false, getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
                 }
             }
         }
@@ -259,8 +280,16 @@
     public String getJoystickDescriptor(InputDevice joystickDevice) {
         return joystickDevice.getName();
     }
+    public int getProductId(InputDevice joystickDevice) {
+        return 0;
+    }
+    public int getVendorId(InputDevice joystickDevice) {
+        return 0;
+    }
+    public int getButtonMask(InputDevice joystickDevice) {
+        return -1;
+    }
 }
-
 
 class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
 
@@ -276,6 +305,144 @@
     }
 }
 
+class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
+
+    @Override
+    public int getProductId(InputDevice joystickDevice) {
+        return joystickDevice.getProductId();
+    }
+
+    @Override
+    public int getVendorId(InputDevice joystickDevice) {
+        return joystickDevice.getVendorId();
+    }
+
+    @Override
+    public int getButtonMask(InputDevice joystickDevice) {
+        int button_mask = 0;
+        int[] keys = new int[] {
+            KeyEvent.KEYCODE_BUTTON_A,
+            KeyEvent.KEYCODE_BUTTON_B,
+            KeyEvent.KEYCODE_BUTTON_X,
+            KeyEvent.KEYCODE_BUTTON_Y,
+            KeyEvent.KEYCODE_BACK,
+            KeyEvent.KEYCODE_BUTTON_MODE,
+            KeyEvent.KEYCODE_BUTTON_START,
+            KeyEvent.KEYCODE_BUTTON_THUMBL,
+            KeyEvent.KEYCODE_BUTTON_THUMBR,
+            KeyEvent.KEYCODE_BUTTON_L1,
+            KeyEvent.KEYCODE_BUTTON_R1,
+            KeyEvent.KEYCODE_DPAD_UP,
+            KeyEvent.KEYCODE_DPAD_DOWN,
+            KeyEvent.KEYCODE_DPAD_LEFT,
+            KeyEvent.KEYCODE_DPAD_RIGHT,
+            KeyEvent.KEYCODE_BUTTON_SELECT,
+            KeyEvent.KEYCODE_DPAD_CENTER,
+
+            // These don't map into any SDL controller buttons directly
+            KeyEvent.KEYCODE_BUTTON_L2,
+            KeyEvent.KEYCODE_BUTTON_R2,
+            KeyEvent.KEYCODE_BUTTON_C,
+            KeyEvent.KEYCODE_BUTTON_Z,
+            KeyEvent.KEYCODE_BUTTON_1,
+            KeyEvent.KEYCODE_BUTTON_2,
+            KeyEvent.KEYCODE_BUTTON_3,
+            KeyEvent.KEYCODE_BUTTON_4,
+            KeyEvent.KEYCODE_BUTTON_5,
+            KeyEvent.KEYCODE_BUTTON_6,
+            KeyEvent.KEYCODE_BUTTON_7,
+            KeyEvent.KEYCODE_BUTTON_8,
+            KeyEvent.KEYCODE_BUTTON_9,
+            KeyEvent.KEYCODE_BUTTON_10,
+            KeyEvent.KEYCODE_BUTTON_11,
+            KeyEvent.KEYCODE_BUTTON_12,
+            KeyEvent.KEYCODE_BUTTON_13,
+            KeyEvent.KEYCODE_BUTTON_14,
+            KeyEvent.KEYCODE_BUTTON_15,
+            KeyEvent.KEYCODE_BUTTON_16,
+        };
+        int[] masks = new int[] {
+            (1 << 0),   // A -> A
+            (1 << 1),   // B -> B
+            (1 << 2),   // X -> X
+            (1 << 3),   // Y -> Y
+            (1 << 4),   // BACK -> BACK
+            (1 << 5),   // MODE -> GUIDE
+            (1 << 6),   // START -> START
+            (1 << 7),   // THUMBL -> LEFTSTICK
+            (1 << 8),   // THUMBR -> RIGHTSTICK
+            (1 << 9),   // L1 -> LEFTSHOULDER
+            (1 << 10),  // R1 -> RIGHTSHOULDER
+            (1 << 11),  // DPAD_UP -> DPAD_UP
+            (1 << 12),  // DPAD_DOWN -> DPAD_DOWN
+            (1 << 13),  // DPAD_LEFT -> DPAD_LEFT
+            (1 << 14),  // DPAD_RIGHT -> DPAD_RIGHT
+            (1 << 4),   // SELECT -> BACK
+            (1 << 0),   // DPAD_CENTER -> A
+            (1 << 15),  // L2 -> ??
+            (1 << 16),  // R2 -> ??
+            (1 << 17),  // C -> ??
+            (1 << 18),  // Z -> ??
+            (1 << 20),  // 1 -> ??
+            (1 << 21),  // 2 -> ??
+            (1 << 22),  // 3 -> ??
+            (1 << 23),  // 4 -> ??
+            (1 << 24),  // 5 -> ??
+            (1 << 25),  // 6 -> ??
+            (1 << 26),  // 7 -> ??
+            (1 << 27),  // 8 -> ??
+            (1 << 28),  // 9 -> ??
+            (1 << 29),  // 10 -> ??
+            (1 << 30),  // 11 -> ??
+            (1 << 31),  // 12 -> ??
+            // We're out of room...
+            0xFFFFFFFF,  // 13 -> ??
+            0xFFFFFFFF,  // 14 -> ??
+            0xFFFFFFFF,  // 15 -> ??
+            0xFFFFFFFF,  // 16 -> ??
+        };
+        boolean[] has_keys = joystickDevice.hasKeys(keys);
+        for (int i = 0; i < keys.length; ++i) {
+            if (has_keys[i]) {
+                button_mask |= masks[i];
+            }
+        }
+        return button_mask;
+    }
+}
+
+class SDLHapticHandler_API26 extends SDLHapticHandler {
+    @Override
+    public void run(int device_id, float intensity, int length) {
+        SDLHaptic haptic = getHaptic(device_id);
+        if (haptic != null) {
+            Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
+            if (intensity == 0.0f) {
+                stop(device_id);
+                return;
+            }
+
+            int vibeValue = Math.round(intensity * 255);
+
+            if (vibeValue > 255) {
+                vibeValue = 255;
+            }
+            if (vibeValue < 1) {
+                stop(device_id);
+                return;
+            }
+            try {
+                haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
+            }
+            catch (Exception e) {
+                // Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
+                // something went horribly wrong with the Android 8.0 APIs.
+                haptic.vib.vibrate(length);
+            }
+        }
+    }
+}
+
 class SDLHapticHandler {
 
     class SDLHaptic {
@@ -285,20 +452,27 @@
     }
 
     private ArrayList<SDLHaptic> mHaptics;
-    
+
     public SDLHapticHandler() {
         mHaptics = new ArrayList<SDLHaptic>();
     }
 
-    public void run(int device_id, int length) {
+    public void run(int device_id, float intensity, int length) {
         SDLHaptic haptic = getHaptic(device_id);
         if (haptic != null) {
-            haptic.vib.vibrate (length);
+            haptic.vib.vibrate(length);
+        }
+    }
+
+    public void stop(int device_id) {
+        SDLHaptic haptic = getHaptic(device_id);
+        if (haptic != null) {
+            haptic.vib.cancel();
         }
     }
 
     public void pollHapticDevices() {
-        
+
         final int deviceId_VIBRATOR_SERVICE = 999999;
         boolean hasVibratorService = false;
 
@@ -342,7 +516,7 @@
                     haptic = new SDLHaptic();
                     haptic.device_id = deviceId_VIBRATOR_SERVICE;
                     haptic.name = "VIBRATOR_SERVICE";
-                    haptic.vib = vib; 
+                    haptic.vib = vib;
                     mHaptics.add(haptic);
                     SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
                 }
@@ -384,7 +558,7 @@
             }
         }
         return null;
-    }   
+    }
 }
 
 class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
@@ -409,14 +583,14 @@
                     case MotionEvent.ACTION_SCROLL:
                         x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
                         y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
-                        SDLActivity.onNativeMouse(0, action, x, y);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
                         return true;
 
                     case MotionEvent.ACTION_HOVER_MOVE:
                         x = event.getX(0);
                         y = event.getY(0);
 
-                        SDLActivity.onNativeMouse(0, action, x, y);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
                         return true;
 
                     default:
@@ -431,5 +605,242 @@
         // Event was not managed
         return false;
     }
+
+    public boolean supportsRelativeMouse() {
+        return false;
+    }
+
+    public boolean inRelativeMode() {
+        return false;
+    }
+
+    public boolean setRelativeMouseEnabled(boolean enabled) {
+        return false;
+    }
+
+    public void reclaimRelativeMouseModeIfNeeded()
+    {
+
+    }
+
+    public float getEventX(MotionEvent event) {
+        return event.getX(0);
+    }
+
+    public float getEventY(MotionEvent event) {
+        return event.getY(0);
+    }
+
 }
 
+class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
+    // Generic Motion (mouse hover, joystick...) events go here
+
+    private boolean mRelativeModeEnabled;
+
+    @Override
+    public boolean onGenericMotion(View v, MotionEvent event) {
+        float x, y;
+        int action;
+
+        switch ( event.getSource() ) {
+            case InputDevice.SOURCE_JOYSTICK:
+            case InputDevice.SOURCE_GAMEPAD:
+            case InputDevice.SOURCE_DPAD:
+                return SDLControllerManager.handleJoystickMotionEvent(event);
+
+            case InputDevice.SOURCE_MOUSE:
+                if (!SDLActivity.mSeparateMouseAndTouch) {
+                    break;
+                }
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        if (mRelativeModeEnabled) {
+                            x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
+                            y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
+                        }
+                        else {
+                            x = event.getX(0);
+                            y = event.getY(0);
+                        }
+
+                        SDLActivity.onNativeMouse(0, action, x, y, mRelativeModeEnabled);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        // Event was not managed
+        return false;
+    }
+
+    @Override
+    public boolean supportsRelativeMouse() {
+        return true;
+    }
+
+    @Override
+    public boolean inRelativeMode() {
+        return mRelativeModeEnabled;
+    }
+
+    @Override
+    public boolean setRelativeMouseEnabled(boolean enabled) {
+        mRelativeModeEnabled = enabled;
+        return true;
+    }
+
+    @Override
+    public float getEventX(MotionEvent event) {
+        if (mRelativeModeEnabled) {
+            return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
+        }
+        else {
+            return event.getX(0);
+        }
+    }
+
+    @Override
+    public float getEventY(MotionEvent event) {
+        if (mRelativeModeEnabled) {
+            return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
+        }
+        else {
+            return event.getY(0);
+        }
+    }
+}
+
+
+class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
+    // Generic Motion (mouse hover, joystick...) events go here
+    private boolean mRelativeModeEnabled;
+
+    @Override
+    public boolean onGenericMotion(View v, MotionEvent event) {
+        float x, y;
+        int action;
+
+        switch ( event.getSource() ) {
+            case InputDevice.SOURCE_JOYSTICK:
+            case InputDevice.SOURCE_GAMEPAD:
+            case InputDevice.SOURCE_DPAD:
+                return SDLControllerManager.handleJoystickMotionEvent(event);
+
+            case InputDevice.SOURCE_MOUSE:
+            case 12290: // DeX desktop mouse cursor is a separate non-standard input type.
+                if (!SDLActivity.mSeparateMouseAndTouch) {
+                    break;
+                }
+
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        x = event.getX(0);
+                        y = event.getY(0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            case InputDevice.SOURCE_MOUSE_RELATIVE:
+                if (!SDLActivity.mSeparateMouseAndTouch) {
+                    break;
+                }
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y, false);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        x = event.getX(0);
+                        y = event.getY(0);
+                        SDLActivity.onNativeMouse(0, action, x, y, true);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        // Event was not managed
+        return false;
+    }
+
+    @Override
+    public boolean supportsRelativeMouse() {
+        return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
+    }
+
+    @Override
+    public boolean inRelativeMode() {
+        return mRelativeModeEnabled;
+    }
+
+    @Override
+    public boolean setRelativeMouseEnabled(boolean enabled) {
+        if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
+            if (enabled) {
+                SDLActivity.getContentView().requestPointerCapture();
+            }
+            else {
+                SDLActivity.getContentView().releasePointerCapture();
+            }
+            mRelativeModeEnabled = enabled;
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    @Override
+    public void reclaimRelativeMouseModeIfNeeded()
+    {
+        if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
+            SDLActivity.getContentView().requestPointerCapture();
+        }
+    }
+
+    @Override
+    public float getEventX(MotionEvent event) {
+        // Relative mouse in capture mode will only have relative for X/Y
+        return event.getX(0);
+    }
+
+    @Override
+    public float getEventY(MotionEvent event) {
+        // Relative mouse in capture mode will only have relative for X/Y
+        return event.getY(0);
+    }
+}
\ No newline at end of file
diff --git a/source/android-project/build.gradle b/source/android-project/build.gradle
index c2eea8e..f6f90b2 100644
--- a/source/android-project/build.gradle
+++ b/source/android-project/build.gradle
@@ -3,9 +3,10 @@
 buildscript {
     repositories {
         jcenter()
+        google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.3'
+        classpath 'com.android.tools.build:gradle:3.2.0'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -15,6 +16,7 @@
 allprojects {
     repositories {
         jcenter()
+        google()
     }
 }
 
diff --git a/source/android-project/gradle/wrapper/gradle-wrapper.properties b/source/android-project/gradle/wrapper/gradle-wrapper.properties
index 6035d0e..f9b3be2 100644
--- a/source/android-project/gradle/wrapper/gradle-wrapper.properties
+++ b/source/android-project/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
diff --git a/source/build-scripts/install-sh b/source/build-scripts/install-sh
index 1a83534..377bb86 100755
--- a/source/build-scripts/install-sh
+++ b/source/build-scripts/install-sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2005-02-02.21
+scriptversion=2011-11-20.07; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -35,42 +35,72 @@
 # FSF changes to this file are in the public domain.
 #
 # Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
+# 'make' implicit rules from creating a file called install from it
 # when there is no Makefile.
 #
 # This script is compatible with the BSD install script, but was written
-# from scratch.  It can only install one file at a time, a restriction
-# shared with many OS's install programs.
+# from scratch.
+
+nl='
+'
+IFS=" ""	$nl"
 
 # set DOITPROG to echo to test this script
 
 # Don't use :- since 4.3BSD and earlier shells don't like it.
-doit="${DOITPROG-}"
+doit=${DOITPROG-}
+if test -z "$doit"; then
+  doit_exec=exec
+else
+  doit_exec=$doit
+fi
 
-# put in absolute paths if you don't have them in your path; or use env. vars.
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
 
-mvprog="${MVPROG-mv}"
-cpprog="${CPPROG-cp}"
-chmodprog="${CHMODPROG-chmod}"
-chownprog="${CHOWNPROG-chown}"
-chgrpprog="${CHGRPPROG-chgrp}"
-stripprog="${STRIPPROG-strip}"
-rmprog="${RMPROG-rm}"
-mkdirprog="${MKDIRPROG-mkdir}"
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
 
-chmodcmd="$chmodprog 0755"
-chowncmd=
+posix_glob='?'
+initialize_posix_glob='
+  test "$posix_glob" != "?" || {
+    if (set -f) 2>/dev/null; then
+      posix_glob=
+    else
+      posix_glob=:
+    fi
+  }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
 chgrpcmd=
-stripcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
 rmcmd="$rmprog -f"
-mvcmd="$mvprog"
+stripcmd=
+
 src=
 dst=
 dir_arg=
-dstarg=
+dst_arg=
+
+copy_on_change=false
 no_target_directory=
 
-usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
    or: $0 [OPTION]... SRCFILES... DIRECTORY
    or: $0 [OPTION]... -t DIRECTORY SRCFILES...
    or: $0 [OPTION]... -d DIRECTORIES...
@@ -80,108 +110,148 @@
 In the 4th, create DIRECTORIES.
 
 Options:
--c         (ignored)
--d         create directories instead of installing files.
--g GROUP   $chgrpprog installed files to GROUP.
--m MODE    $chmodprog installed files to MODE.
--o USER    $chownprog installed files to USER.
--s         $stripprog installed files.
--t DIRECTORY  install into DIRECTORY.
--T         report an error if DSTFILE is a directory.
---help     display this help and exit.
---version  display version info and exit.
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
 
 Environment variables override the default commands:
-  CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
 "
 
-while test -n "$1"; do
+while test $# -ne 0; do
   case $1 in
-    -c) shift
-        continue;;
+    -c) ;;
 
-    -d) dir_arg=true
-        shift
-        continue;;
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
 
     -g) chgrpcmd="$chgrpprog $2"
-        shift
-        shift
-        continue;;
+	shift;;
 
     --help) echo "$usage"; exit $?;;
 
-    -m) chmodcmd="$chmodprog $2"
-        shift
-        shift
-        continue;;
+    -m) mode=$2
+	case $mode in
+	  *' '* | *'	'* | *'
+'*	  | *'*'* | *'?'* | *'['*)
+	    echo "$0: invalid mode: $mode" >&2
+	    exit 1;;
+	esac
+	shift;;
 
     -o) chowncmd="$chownprog $2"
-        shift
-        shift
-        continue;;
+	shift;;
 
-    -s) stripcmd=$stripprog
-        shift
-        continue;;
+    -s) stripcmd=$stripprog;;
 
-    -t) dstarg=$2
-	shift
-	shift
-	continue;;
+    -t) dst_arg=$2
+	# Protect names problematic for 'test' and other utilities.
+	case $dst_arg in
+	  -* | [=\(\)!]) dst_arg=./$dst_arg;;
+	esac
+	shift;;
 
-    -T) no_target_directory=true
-	shift
-	continue;;
+    -T) no_target_directory=true;;
 
     --version) echo "$0 $scriptversion"; exit $?;;
 
-    *)  # When -d is used, all remaining arguments are directories to create.
-	# When -t is used, the destination is already specified.
-	test -n "$dir_arg$dstarg" && break
-        # Otherwise, the last argument is the destination.  Remove it from $@.
-	for arg
-	do
-          if test -n "$dstarg"; then
-	    # $@ is not empty: it contains at least $arg.
-	    set fnord "$@" "$dstarg"
-	    shift # fnord
-	  fi
-	  shift # arg
-	  dstarg=$arg
-	done
+    --)	shift
 	break;;
+
+    -*)	echo "$0: invalid option: $1" >&2
+	exit 1;;
+
+    *)  break;;
   esac
+  shift
 done
 
-if test -z "$1"; then
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
   if test -z "$dir_arg"; then
     echo "$0: no input file specified." >&2
     exit 1
   fi
-  # It's OK to call `install-sh -d' without argument.
+  # It's OK to call 'install-sh -d' without argument.
   # This can happen when creating conditional directories.
   exit 0
 fi
 
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+	u_plus_rw=
+      else
+	u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+	u_plus_rw=
+      else
+	u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
 for src
 do
-  # Protect names starting with `-'.
+  # Protect names problematic for 'test' and other utilities.
   case $src in
-    -*) src=./$src ;;
+    -* | [=\(\)!]) src=./$src;;
   esac
 
   if test -n "$dir_arg"; then
     dst=$src
-    src=
-
-    if test -d "$dst"; then
-      mkdircmd=:
-      chmodcmd=
-    else
-      mkdircmd=$mkdirprog
-    fi
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
   else
+
     # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
     # might cause directories to be created, which would be especially bad
     # if $src (and thus $dsttmp) contains '*'.
@@ -190,71 +260,194 @@
       exit 1
     fi
 
-    if test -z "$dstarg"; then
+    if test -z "$dst_arg"; then
       echo "$0: no destination specified." >&2
       exit 1
     fi
-
-    dst=$dstarg
-    # Protect names starting with `-'.
-    case $dst in
-      -*) dst=./$dst ;;
-    esac
+    dst=$dst_arg
 
     # If destination is a directory, append the input filename; won't work
     # if double slashes aren't ignored.
     if test -d "$dst"; then
       if test -n "$no_target_directory"; then
-	echo "$0: $dstarg: Is a directory" >&2
+	echo "$0: $dst_arg: Is a directory" >&2
 	exit 1
       fi
-      dst=$dst/`basename "$src"`
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      # Prefer dirname, but fall back on a substitute if dirname fails.
+      dstdir=`
+	(dirname "$dst") 2>/dev/null ||
+	expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	     X"$dst" : 'X\(//\)[^/]' \| \
+	     X"$dst" : 'X\(//\)$' \| \
+	     X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+	echo X"$dst" |
+	    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+		   s//\1/
+		   q
+		 }
+		 /^X\(\/\/\)[^/].*/{
+		   s//\1/
+		   q
+		 }
+		 /^X\(\/\/\)$/{
+		   s//\1/
+		   q
+		 }
+		 /^X\(\/\).*/{
+		   s//\1/
+		   q
+		 }
+		 s/.*/./; q'
+      `
+
+      test -d "$dstdir"
+      dstdir_status=$?
     fi
   fi
 
-  # This sed command emulates the dirname command.
-  dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
+  obsolete_mkdir_used=false
 
-  # Make sure that the destination directory exists.
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+	# Create intermediate dirs using mode 755 as modified by the umask.
+	# This is like FreeBSD 'install' as of 1997-10-28.
+	umask=`umask`
+	case $stripcmd.$umask in
+	  # Optimize common cases.
+	  *[2367][2367]) mkdir_umask=$umask;;
+	  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
 
-  # Skip lots of stat calls in the usual case.
-  if test ! -d "$dstdir"; then
-    defaultIFS='
-	 '
-    IFS="${IFS-$defaultIFS}"
+	  *[0-7])
+	    mkdir_umask=`expr $umask + 22 \
+	      - $umask % 100 % 40 + $umask % 20 \
+	      - $umask % 10 % 4 + $umask % 2
+	    `;;
+	  *) mkdir_umask=$umask,go-w;;
+	esac
 
-    oIFS=$IFS
-    # Some sh's can't handle IFS=/ for some reason.
-    IFS='%'
-    set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
-    shift
-    IFS=$oIFS
+	# With -d, create the new directory with the user-specified mode.
+	# Otherwise, rely on $mkdir_umask.
+	if test -n "$dir_arg"; then
+	  mkdir_mode=-m$mode
+	else
+	  mkdir_mode=
+	fi
 
-    pathcomp=
+	posix_mkdir=false
+	case $umask in
+	  *[123567][0-7][0-7])
+	    # POSIX mkdir -p sets u+wx bits regardless of umask, which
+	    # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+	    ;;
+	  *)
+	    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+	    trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
 
-    while test $# -ne 0 ; do
-      pathcomp=$pathcomp$1
+	    if (umask $mkdir_umask &&
+		exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+	    then
+	      if test -z "$dir_arg" || {
+		   # Check for POSIX incompatibilities with -m.
+		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+		   # other-writable bit of parent directory when it shouldn't.
+		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+		   ls_ld_tmpdir=`ls -ld "$tmpdir"`
+		   case $ls_ld_tmpdir in
+		     d????-?r-*) different_mode=700;;
+		     d????-?--*) different_mode=755;;
+		     *) false;;
+		   esac &&
+		   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+		     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+		     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+		   }
+		 }
+	      then posix_mkdir=:
+	      fi
+	      rmdir "$tmpdir/d" "$tmpdir"
+	    else
+	      # Remove any dirs left behind by ancient mkdir implementations.
+	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+	    fi
+	    trap '' 0;;
+	esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+	umask $mkdir_umask &&
+	$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+	/*) prefix='/';;
+	[-=\(\)!]*) prefix='./';;
+	*)  prefix='';;
+      esac
+
+      eval "$initialize_posix_glob"
+
+      oIFS=$IFS
+      IFS=/
+      $posix_glob set -f
+      set fnord $dstdir
       shift
-      if test ! -d "$pathcomp"; then
-        $mkdirprog "$pathcomp"
-	# mkdir can fail with a `File exist' error in case several
-	# install-sh are creating the directory concurrently.  This
-	# is OK.
-	test -d "$pathcomp" || exit
+      $posix_glob set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+	test X"$d" = X && continue
+
+	prefix=$prefix$d
+	if test -d "$prefix"; then
+	  prefixes=
+	else
+	  if $posix_mkdir; then
+	    (umask=$mkdir_umask &&
+	     $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+	    # Don't fail if two instances are running concurrently.
+	    test -d "$prefix" || exit 1
+	  else
+	    case $prefix in
+	      *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+	      *) qprefix=$prefix;;
+	    esac
+	    prefixes="$prefixes '$qprefix'"
+	  fi
+	fi
+	prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+	# Don't fail if two instances are running concurrently.
+	(umask $mkdir_umask &&
+	 eval "\$doit_exec \$mkdirprog $prefixes") ||
+	  test -d "$dstdir" || exit 1
+	obsolete_mkdir_used=true
       fi
-      pathcomp=$pathcomp/
-    done
+    fi
   fi
 
   if test -n "$dir_arg"; then
-    $doit $mkdircmd "$dst" \
-      && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
-      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
-      && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
-      && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
-
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
   else
-    dstfile=`basename "$dst"`
 
     # Make a couple of temp file names in the proper directory.
     dsttmp=$dstdir/_inst.$$_
@@ -262,10 +455,9 @@
 
     # Trap to clean up those temp files at exit.
     trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
-    trap '(exit $?); exit' 1 2 13 15
 
     # Copy the file name to the temp name.
-    $doit $cpprog "$src" "$dsttmp" &&
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
 
     # and set any options; do chmod last to preserve setuid bits.
     #
@@ -273,51 +465,63 @@
     # ignore errors from any of these, just make sure not to ignore
     # errors from the above "$doit $cpprog $src $dsttmp" command.
     #
-    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
-      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
-      && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
-      && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
 
-    # Now rename the file to the real destination.
-    { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
-      || {
-	   # The rename failed, perhaps because mv can't rename something else
-	   # to itself, or perhaps because mv is so ancient that it does not
-	   # support -f.
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"	2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"	2>/dev/null` &&
 
-	   # Now remove or move aside any old file at destination location.
-	   # We try this two ways since rm can't unlink itself on some
-	   # systems and the destination file might be busy for other
-	   # reasons.  In this case, the final cleanup might fail but the new
-	   # file should still install successfully.
-	   {
-	     if test -f "$dstdir/$dstfile"; then
-	       $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
-	       || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
-	       || {
-		 echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
-		 (exit 1); exit 1
-	       }
-	     else
-	       :
-	     fi
-	   } &&
+       eval "$initialize_posix_glob" &&
+       $posix_glob set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       $posix_glob set +f &&
 
-	   # Now rename the file to the real destination.
-	   $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
-	 }
-    }
-  fi || { (exit 1); exit 1; }
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+	# Now remove or move aside any old file at destination location.
+	# We try this two ways since rm can't unlink itself on some
+	# systems and the destination file might be busy for other
+	# reasons.  In this case, the final cleanup might fail but the new
+	# file should still install successfully.
+	{
+	  test ! -f "$dst" ||
+	  $doit $rmcmd -f "$dst" 2>/dev/null ||
+	  { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+	    { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+	  } ||
+	  { echo "$0: cannot unlink or rename $dst" >&2
+	    (exit 1); exit 1
+	  }
+	} &&
+
+	# Now rename the file to the real destination.
+	$doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
 done
-
-# The final little trick to "correctly" pass the exit status to the exit trap.
-{
-  (exit 0); exit 0
-}
 
 # Local variables:
 # eval: (add-hook 'write-file-hooks 'time-stamp)
 # time-stamp-start: "scriptversion="
 # time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
 # End:
diff --git a/source/build-scripts/ltmain.sh b/source/build-scripts/ltmain.sh
index 6635343..3cbc4a7 100755
--- a/source/build-scripts/ltmain.sh
+++ b/source/build-scripts/ltmain.sh
@@ -7404,8 +7404,11 @@
 	  # Darwin ld doesn't like 0 for these options...
 	  func_arith $current + 1
 	  minor_current=$func_arith_result
-	  xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
-	  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+	  #xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+	  #verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+	  # make the compatibility version match the Xcode project files, i.e. 1.0
+	  xlcverstring="${wl}-compatibility_version 1.0 ${wl}-current_version ${wl}$minor_current.$revision"
+	  verstring="-compatibility_version 1.0 -current_version $minor_current.$revision"
 	  ;;
 
 	freebsd-aout)
diff --git a/source/build-scripts/mkinstalldirs b/source/build-scripts/mkinstalldirs
index 8ab885e..55d537f 100755
--- a/source/build-scripts/mkinstalldirs
+++ b/source/build-scripts/mkinstalldirs
@@ -1,29 +1,59 @@
 #! /bin/sh
 # mkinstalldirs --- make directory hierarchy
-# Author: Noah Friedman <friedman@prep.ai.mit.edu>
-# Created: 1993-05-16
-# Public domain
 
+scriptversion=2009-04-28.21; # UTC
+
+# Original author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain.
+#
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+IFS=" ""	$nl"
 errstatus=0
-dirmode=""
+dirmode=
 
 usage="\
-Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..."
+Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ...
+
+Create each directory DIR (with mode MODE, if specified), including all
+leading file name components.
+
+Report bugs to <bug-automake@gnu.org>."
 
 # process command line arguments
 while test $# -gt 0 ; do
-   case "${1}" in
-     -h | --help | --h* )			# -h for help
-	echo "${usage}" 1>&2; exit 0 ;;
-     -m )					# -m PERM arg
-	shift
-	test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; }
-	dirmode="${1}"
-	shift ;;
-     -- ) shift; break ;;			# stop option processing
-     -* ) echo "${usage}" 1>&2; exit 1 ;;	# unknown option
-     * )  break ;;				# first non-opt arg
-   esac
+  case $1 in
+    -h | --help | --h*)         # -h for help
+      echo "$usage"
+      exit $?
+      ;;
+    -m)                         # -m PERM arg
+      shift
+      test $# -eq 0 && { echo "$usage" 1>&2; exit 1; }
+      dirmode=$1
+      shift
+      ;;
+    --version)
+      echo "$0 $scriptversion"
+      exit $?
+      ;;
+    --)                         # stop option processing
+      shift
+      break
+      ;;
+    -*)                         # unknown option
+      echo "$usage" 1>&2
+      exit 1
+      ;;
+    *)                          # first non-opt arg
+      break
+      ;;
+  esac
 done
 
 for file
@@ -36,64 +66,97 @@
 done
 
 case $# in
-0) exit 0 ;;
+  0) exit 0 ;;
 esac
 
+# Solaris 8's mkdir -p isn't thread-safe.  If you mkdir -p a/b and
+# mkdir -p a/c at the same time, both will detect that a is missing,
+# one will create a, then the other will try to create a and die with
+# a "File exists" error.  This is a problem when calling mkinstalldirs
+# from a parallel make.  We use --version in the probe to restrict
+# ourselves to GNU mkdir, which is thread-safe.
 case $dirmode in
-'')
-  if mkdir -p -- . 2>/dev/null; then
-    echo "mkdir -p -- $*"
-    exec mkdir -p -- "$@"
-  fi ;;
-*)
-  if mkdir -m "$dirmode" -p -- . 2>/dev/null; then
-    echo "mkdir -m $dirmode -p -- $*"
-    exec mkdir -m "$dirmode" -p -- "$@"
-  fi ;;
+  '')
+    if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+      echo "mkdir -p -- $*"
+      exec mkdir -p -- "$@"
+    else
+      # On NextStep and OpenStep, the 'mkdir' command does not
+      # recognize any option.  It will interpret all options as
+      # directories to create, and then abort because '.' already
+      # exists.
+      test -d ./-p && rmdir ./-p
+      test -d ./--version && rmdir ./--version
+    fi
+    ;;
+  *)
+    if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 &&
+       test ! -d ./--version; then
+      echo "mkdir -m $dirmode -p -- $*"
+      exec mkdir -m "$dirmode" -p -- "$@"
+    else
+      # Clean up after NextStep and OpenStep mkdir.
+      for d in ./-m ./-p ./--version "./$dirmode";
+      do
+        test -d $d && rmdir $d
+      done
+    fi
+    ;;
 esac
 
 for file
 do
-   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
-   shift
+  case $file in
+    /*) pathcomp=/ ;;
+    *)  pathcomp= ;;
+  esac
+  oIFS=$IFS
+  IFS=/
+  set fnord $file
+  shift
+  IFS=$oIFS
 
-   pathcomp=
-   for d
-   do
-     pathcomp="$pathcomp$d"
-     case "$pathcomp" in
-       -* ) pathcomp=./$pathcomp ;;
-     esac
+  for d
+  do
+    test "x$d" = x && continue
 
-     if test ! -d "$pathcomp"; then
-	echo "mkdir $pathcomp"
+    pathcomp=$pathcomp$d
+    case $pathcomp in
+      -*) pathcomp=./$pathcomp ;;
+    esac
 
-	mkdir "$pathcomp" || lasterr=$?
+    if test ! -d "$pathcomp"; then
+      echo "mkdir $pathcomp"
 
-	if test ! -d "$pathcomp"; then
-	  errstatus=$lasterr
-	else
-	  if test ! -z "$dirmode"; then
-	     echo "chmod $dirmode $pathcomp"
+      mkdir "$pathcomp" || lasterr=$?
 
-	     lasterr=""
-	     chmod "$dirmode" "$pathcomp" || lasterr=$?
+      if test ! -d "$pathcomp"; then
+	errstatus=$lasterr
+      else
+	if test ! -z "$dirmode"; then
+	  echo "chmod $dirmode $pathcomp"
+	  lasterr=
+	  chmod "$dirmode" "$pathcomp" || lasterr=$?
 
-	     if test ! -z "$lasterr"; then
-	       errstatus=$lasterr
-	     fi
+	  if test ! -z "$lasterr"; then
+	    errstatus=$lasterr
 	  fi
 	fi
-     fi
+      fi
+    fi
 
-     pathcomp="$pathcomp/"
-   done
+    pathcomp=$pathcomp/
+  done
 done
 
 exit $errstatus
 
 # Local Variables:
 # mode: shell-script
-# sh-indentation: 3
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
 # End:
-# mkinstalldirs ends here
diff --git a/source/build-scripts/winrtbuild.ps1 b/source/build-scripts/winrtbuild.ps1
index 49e6e5a..0d7a24e 100644
--- a/source/build-scripts/winrtbuild.ps1
+++ b/source/build-scripts/winrtbuild.ps1
@@ -39,7 +39,7 @@
 #
 
 # Base version of SDL, used for packaging purposes
-$SDLVersion = "2.0.7"
+$SDLVersion = "2.0.9"
 
 # Gets the .bat file that sets up an MSBuild environment, given one of
 # Visual Studio's, "PlatformToolset"s.
diff --git a/source/cmake/sdlchecks.cmake b/source/cmake/sdlchecks.cmake
index b822c7a..4a2c3ed 100644
--- a/source/cmake/sdlchecks.cmake
+++ b/source/cmake/sdlchecks.cmake
@@ -381,9 +381,21 @@
         FindLibraryAndSONAME("${_LIB}")
     endforeach()
 
-    find_path(X_INCLUDEDIR X11/Xlib.h)
+    find_path(X_INCLUDEDIR X11/Xlib.h
+        /usr/pkg/xorg/include
+        /usr/X11R6/include
+        /usr/X11R7/include
+        /usr/local/include/X11
+        /usr/include/X11
+        /usr/openwin/include
+        /usr/openwin/share/include
+        /opt/graphics/OpenGL/include
+        /opt/X11/include
+    )
+
     if(X_INCLUDEDIR)
-      set(X_CFLAGS "-I${X_INCLUDEDIR}")
+      list(APPEND EXTRA_CFLAGS "-I${X_INCLUDEDIR}")
+      list(APPEND CMAKE_REQUIRED_INCLUDES "${X_INCLUDEDIR}")
     endif()
 
     check_include_file(X11/Xcursor/Xcursor.h HAVE_XCURSOR_H)
@@ -420,7 +432,7 @@
         endif()
         if(NOT HAVE_SHMAT)
           add_definitions(-DNO_SHARED_MEMORY)
-          set(X_CFLAGS "${X_CFLAGS} -DNO_SHARED_MEMORY")
+          list(APPEND EXTRA_CFLAGS "-DNO_SHARED_MEMORY")
         endif()
       endif()
 
@@ -438,8 +450,6 @@
           list(APPEND EXTRA_LIBS ${X11_LIB} ${XEXT_LIB})
         endif()
       endif()
-
-      set(SDL_CFLAGS "${SDL_CFLAGS} ${X_CFLAGS}")
 
       set(CMAKE_REQUIRED_LIBRARIES ${X11_LIB} ${X11_LIB})
       check_c_source_compiles("
@@ -625,35 +635,6 @@
   if(VIDEO_WAYLAND)
     pkg_check_modules(WAYLAND wayland-client wayland-scanner wayland-protocols wayland-egl wayland-cursor egl xkbcommon)
 
-    # We have to generate some protocol interface code for some various Wayland features.
-    if(WAYLAND_FOUND)
-      execute_process(
-        COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-client
-        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
-        RESULT_VARIABLE WAYLAND_CORE_PROTOCOL_DIR_RC
-        OUTPUT_VARIABLE WAYLAND_CORE_PROTOCOL_DIR
-        ERROR_QUIET
-        OUTPUT_STRIP_TRAILING_WHITESPACE
-      )
-      if(NOT WAYLAND_CORE_PROTOCOL_DIR_RC EQUAL 0)
-        set(WAYLAND_FOUND FALSE)
-      endif()
-    endif()
-
-    if(WAYLAND_FOUND)
-      execute_process(
-        COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols
-        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
-        RESULT_VARIABLE WAYLAND_PROTOCOLS_DIR_RC
-        OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR
-        ERROR_QUIET
-        OUTPUT_STRIP_TRAILING_WHITESPACE
-      )
-      if(NOT WAYLAND_PROTOCOLS_DIR_RC EQUAL 0)
-        set(WAYLAND_FOUND FALSE)
-      endif()
-    endif()
-
     if(WAYLAND_FOUND)
       execute_process(
         COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=wayland_scanner wayland-scanner
@@ -685,11 +666,10 @@
       file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols")
       include_directories("${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols")
 
-      WaylandProtocolGen("${WAYLAND_SCANNER}" "${WAYLAND_CORE_PROTOCOL_DIR}/wayland.xml" "wayland")
-
-      foreach(_PROTL relative-pointer-unstable-v1 pointer-constraints-unstable-v1 xdg-shell-unstable-v6)
-        string(REGEX REPLACE "\\-unstable\\-.*$" "" PROTSUBDIR ${_PROTL})
-        WaylandProtocolGen("${WAYLAND_SCANNER}" "${WAYLAND_PROTOCOLS_DIR}/unstable/${PROTSUBDIR}/${_PROTL}.xml" "${_PROTL}")
+      file(GLOB WAYLAND_PROTOCOLS_XML RELATIVE "${SDL2_SOURCE_DIR}/wayland-protocols/" "${SDL2_SOURCE_DIR}/wayland-protocols/*.xml")
+      foreach(_XML ${WAYLAND_PROTOCOLS_XML})
+        string(REGEX REPLACE "\\.xml$" "" _PROTL "${_XML}")
+        WaylandProtocolGen("${WAYLAND_SCANNER}" "${SDL2_SOURCE_DIR}/wayland-protocols/${_XML}" "${_PROTL}")
       endforeach()
 
       if(VIDEO_WAYLAND_QT_TOUCH)
diff --git a/source/configure b/source/configure
index 1c7f87e..aee0cb6 100755
--- a/source/configure
+++ b/source/configure
@@ -785,6 +785,7 @@
 enable_events
 enable_joystick
 enable_haptic
+enable_sensor
 enable_power
 enable_filesystem
 enable_threads
@@ -866,7 +867,9 @@
 enable_pthreads
 enable_pthread_sem
 enable_directx
+enable_wasapi
 enable_sdl_dlopen
+enable_hidapi
 enable_clock_gettime
 enable_rpath
 enable_render_d3d
@@ -1521,6 +1524,7 @@
   --enable-joystick       Enable the joystick subsystem [[default=yes]]
   --enable-haptic         Enable the haptic (force feedback) subsystem
                           [[default=yes]]
+  --enable-sensor         Enable the sensor subsystem [[default=yes]]
   --enable-power          Enable the power subsystem [[default=yes]]
   --enable-filesystem     Enable the filesystem subsystem [[default=yes]]
   --enable-threads        Enable the threading subsystem [[default=yes]]
@@ -1572,7 +1576,7 @@
                           QtWayland server support for Wayland video driver
                           [[default=yes]]
   --enable-wayland-shared dynamically load Wayland support [[default=maybe]]
-  --enable-video-mir      use Mir video driver [[default=yes]]
+  --enable-video-mir      use Mir video driver [[default=no]]
   --enable-mir-shared     dynamically load Mir support [[default=maybe]]
   --enable-video-rpi      use Raspberry Pi video driver [[default=yes]]
   --enable-video-x11      use X11 video driver [[default=yes]]
@@ -1620,7 +1624,10 @@
                           [[default=yes]]
   --enable-pthread-sem    use pthread semaphores [[default=yes]]
   --enable-directx        use DirectX for Windows audio/video [[default=yes]]
+  --enable-wasapi         use the Windows WASAPI audio driver [[default=yes]]
   --enable-sdl-dlopen     use dlopen for shared object loading [[default=yes]]
+  --enable-hidapi         use HIDAPI for low level joystick drivers
+                          [[default=no]]
   --enable-clock_gettime  use clock_gettime() instead of gettimeofday() on
                           UNIX [[default=yes]]
   --enable-rpath          use an rpath when linking SDL [[default=yes]]
@@ -2712,9 +2719,9 @@
 #
 SDL_MAJOR_VERSION=2
 SDL_MINOR_VERSION=0
-SDL_MICRO_VERSION=8
+SDL_MICRO_VERSION=9
 SDL_INTERFACE_AGE=0
-SDL_BINARY_AGE=8
+SDL_BINARY_AGE=9
 SDL_VERSION=$SDL_MAJOR_VERSION.$SDL_MINOR_VERSION.$SDL_MICRO_VERSION
 
 
@@ -15752,10 +15759,17 @@
 #    fi
 #done
 SDL_CFLAGS="$BASE_CFLAGS"
-SDL_LIBS="-lSDL2 $BASE_LDFLAGS"
-CPPFLAGS="$CPPFLAGS $EXTRA_CFLAGS"
-CFLAGS="$CFLAGS $EXTRA_CFLAGS"
-LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS"
+SDL_LIBS="-lSDL2"
+if test "x$BASE_LDFLAGS" != x; then
+    SDL_LIBS="$SDL_LIBS $BASE_LDFLAGS"
+fi
+if test "x$EXTRA_CFLAGS" != x; then
+    CPPFLAGS="$CPPFLAGS $EXTRA_CFLAGS"
+    CFLAGS="$CFLAGS $EXTRA_CFLAGS"
+fi
+if test "x$EXTRA_LDFLAGS" != x; then
+    LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS"
+fi
 
 base_libdir=`echo \${libdir} | sed 's/.*\/\(.*\)/\1/; q'`
 
@@ -16690,7 +16704,7 @@
   LIBS="$LIBS -lm"; EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lm"
 fi
 
-    for ac_func in acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf copysign copysignf cos cosf fabs fabsf floor floorf fmod fmodf log logf log10 log10f pow powf scalbn scalbnf sin sinf sqrt sqrtf tan tanf
+    for ac_func in acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf copysign copysignf cos cosf exp expf fabs fabsf floor floorf fmod fmodf log logf log10 log10f pow powf scalbn scalbnf sin sinf sqrt sqrtf tan tanf
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -16764,7 +16778,7 @@
 fi
 
 
-	    for ac_header in libunwind.h
+        for ac_header in libunwind.h
 do :
   ac_fn_c_check_header_mongrel "$LINENO" "libunwind.h" "ac_cv_header_libunwind_h" "$ac_includes_default"
 if test "x$ac_cv_header_libunwind_h" = xyes; then :
@@ -16872,6 +16886,7 @@
 #SOURCES="$SOURCES $srcdir/src/filesystem/*.c"
 SOURCES="$SOURCES $srcdir/src/render/*.c"
 SOURCES="$SOURCES $srcdir/src/render/*/*.c"
+SOURCES="$SOURCES $srcdir/src/sensor/*.c"
 SOURCES="$SOURCES $srcdir/src/stdlib/*.c"
 SOURCES="$SOURCES $srcdir/src/thread/*.c"
 SOURCES="$SOURCES $srcdir/src/timer/*.c"
@@ -16976,6 +16991,20 @@
 
 else
     SUMMARY_modules="${SUMMARY_modules} haptic"
+fi
+# Check whether --enable-sensor was given.
+if test "${enable_sensor+set}" = set; then :
+  enableval=$enable_sensor;
+else
+  enable_sensor=yes
+fi
+
+if test x$enable_sensor != xyes; then
+
+$as_echo "#define SDL_SENSOR_DISABLED 1" >>confdefs.h
+
+else
+    SUMMARY_modules="${SUMMARY_modules} sensor"
 fi
 # Check whether --enable-power was given.
 if test "${enable_power+set}" = set; then :
@@ -18368,7 +18397,7 @@
         { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support" >&5
 $as_echo_n "checking for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support... " >&6; }
         if test x$PKG_CONFIG != xno; then
-        if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
+            if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
                 PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags libpulse-simple`
                 PULSEAUDIO_LIBS=`$PKG_CONFIG --libs libpulse-simple`
                 audio_pulseaudio=yes
@@ -19202,8 +19231,6 @@
                 WAYLAND_CFLAGS=`$PKG_CONFIG --cflags wayland-client wayland-egl wayland-cursor xkbcommon`
                 WAYLAND_LIBS=`$PKG_CONFIG --libs wayland-client wayland-egl wayland-cursor xkbcommon`
                 WAYLAND_SCANNER=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner`
-                WAYLAND_CORE_PROTOCOL_DIR=`$PKG_CONFIG --variable=pkgdatadir wayland-client`
-                WAYLAND_PROTOCOLS_DIR=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`
                 video_wayland=yes
             fi
         fi
@@ -19220,9 +19247,8 @@
 
             fi
 
-            WAYLAND_PROTOCOLS_UNSTABLE="relative-pointer-unstable-v1 pointer-constraints-unstable-v1 xdg-shell-unstable-v6"
-
-            SOURCES="$SOURCES $srcdir/src/video/wayland/*.c"
+            WAYLAND_SOURCES="$srcdir/src/video/wayland/*.c"
+            SOURCES="$SOURCES $WAYLAND_SOURCES"
             EXTRA_CFLAGS="$EXTRA_CFLAGS $WAYLAND_CFLAGS -I\$(gen)"
             # Check whether --enable-wayland-shared was given.
 if test "${enable_wayland_shared+set}" = set; then :
@@ -19300,7 +19326,7 @@
 if test "${enable_video_mir+set}" = set; then :
   enableval=$enable_video_mir;
 else
-  enable_video_mir=yes
+  enable_video_mir=no
 fi
 
 
@@ -20457,7 +20483,7 @@
             cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-              	#include <X11/Xlib.h>
+                #include <X11/Xlib.h>
 
 int
 main ()
@@ -20801,13 +20827,13 @@
 
                 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xinput2 multitouch" >&5
 $as_echo_n "checking for xinput2 multitouch... " >&6; }
-            	have_xinput2_multitouch=no
-            	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+                have_xinput2_multitouch=no
+                cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-              		#include <X11/Xlib.h>
-             		#include <X11/Xproto.h>
-			#include <X11/extensions/XInput2.h>
+                    #include <X11/Xlib.h>
+                    #include <X11/Xproto.h>
+                    #include <X11/extensions/XInput2.h>
 
 int
 main ()
@@ -20822,14 +20848,14 @@
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
 
-            	have_xinput2_multitouch=yes
-            	$as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1" >>confdefs.h
+                    have_xinput2_multitouch=yes
+                    $as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1" >>confdefs.h
 
-                SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
+                    SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
 
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-            	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_xinput2_multitouch" >&5
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_xinput2_multitouch" >&5
 $as_echo "$have_xinput2_multitouch" >&6; }
             fi
             # Check whether --enable-video-x11-xrandr was given.
@@ -21475,6 +21501,9 @@
 
 $as_echo "#define SDL_VIDEO_DRIVER_DIRECTFB 1" >>confdefs.h
 
+
+$as_echo "#define SDL_VIDEO_RENDER_DIRECTFB 1" >>confdefs.h
+
             SOURCES="$SOURCES $srcdir/src/video/directfb/*.c"
             EXTRA_CFLAGS="$EXTRA_CFLAGS $DIRECTFB_CFLAGS"
 
@@ -22015,6 +22044,24 @@
 $as_echo "#define SDL_VIDEO_RENDER_OGL 1" >>confdefs.h
 
         SUMMARY_video="${SUMMARY_video} opengl"
+    fi
+}
+
+CheckMacGLES()
+{
+    if test x$enable_video = xyes -a x$enable_video_opengles = xyes; then
+        video_opengl_egl=yes
+
+$as_echo "#define SDL_VIDEO_OPENGL_EGL 1" >>confdefs.h
+
+        video_opengles_v2=yes
+
+$as_echo "#define SDL_VIDEO_OPENGL_ES2 1" >>confdefs.h
+
+
+$as_echo "#define SDL_VIDEO_RENDER_OGL_ES2 1" >>confdefs.h
+
+        SUMMARY_video="${SUMMARY_video} opengl_es2"
     fi
 }
 
@@ -23118,20 +23165,6 @@
 fi
 
 
-        ac_fn_c_check_header_mongrel "$LINENO" "mmdeviceapi.h" "ac_cv_header_mmdeviceapi_h" "$ac_includes_default"
-if test "x$ac_cv_header_mmdeviceapi_h" = xyes; then :
-  have_wasapi=yes
-fi
-
-
-        ac_fn_c_check_header_mongrel "$LINENO" "audioclient.h" "ac_cv_header_audioclient_h" "$ac_includes_default"
-if test "x$ac_cv_header_audioclient_h" = xyes; then :
-
-else
-  have_wasapi=no
-fi
-
-
         cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -23218,6 +23251,45 @@
             ;;
         esac
     fi
+
+    ac_fn_c_check_header_mongrel "$LINENO" "mmdeviceapi.h" "ac_cv_header_mmdeviceapi_h" "$ac_includes_default"
+if test "x$ac_cv_header_mmdeviceapi_h" = xyes; then :
+  have_wasapi=yes
+fi
+
+
+    if test x$have_wasapi = xyes; then
+        $as_echo "#define HAVE_MMDEVICEAPI_H 1" >>confdefs.h
+
+    fi
+    ac_fn_c_check_header_mongrel "$LINENO" "audioclient.h" "ac_cv_header_audioclient_h" "$ac_includes_default"
+if test "x$ac_cv_header_audioclient_h" = xyes; then :
+
+else
+  have_wasapi=no
+fi
+
+
+    if test x$have_wasapi = xyes; then
+        $as_echo "#define HAVE_AUDIOCLIENT_H 1" >>confdefs.h
+
+    fi
+
+    ac_fn_c_check_header_mongrel "$LINENO" "endpointvolume.h" "ac_cv_header_endpointvolume_h" "$ac_includes_default"
+if test "x$ac_cv_header_endpointvolume_h" = xyes; then :
+  $as_echo "#define HAVE_ENDPOINTVOLUME_H 1" >>confdefs.h
+
+fi
+
+
+
+    # Check whether --enable-wasapi was given.
+if test "${enable_wasapi+set}" = set; then :
+  enableval=$enable_wasapi;
+else
+  enable_wasapi=yes
+fi
+
 }
 
 CheckDLOPEN()
@@ -23696,6 +23768,93 @@
     esac
 }
 
+CheckHIDAPI()
+{
+    # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
+    # so we'll just use libusb when it's available.
+    #
+    # Except that libusb requires root permissions to open devices, so that's not generally useful, and we'll disable this by default.
+    # Check whether --enable-hidapi was given.
+if test "${enable_hidapi+set}" = set; then :
+  enableval=$enable_hidapi;
+else
+  enable_hidapi=no
+fi
+
+    if test x$enable_joystick = xyes -a x$enable_hidapi = xyes; then
+        hidapi_support=no
+        # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+        if test x$PKG_CONFIG != xno; then
+            LIBUSB_CFLAGS=`$PKG_CONFIG --cflags libusb-1.0`
+            LIBUSB_LDFLAGS=`$PKG_CONFIG --libs libusb-1.0`
+            save_CFLAGS="$CFLAGS"
+            CFLAGS="$save_CFLAGS $LIBUSB_CFLAGS"
+            ac_fn_c_check_header_mongrel "$LINENO" "libusb.h" "ac_cv_header_libusb_h" "$ac_includes_default"
+if test "x$ac_cv_header_libusb_h" = xyes; then :
+  have_libusb_h=yes
+fi
+
+
+            CFLAGS="$save_CFLAGS"
+        fi
+        if test x$have_libusb_h = xyes; then
+            hidapi_support=yes
+
+$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
+
+            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
+            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+            SOURCES="$SOURCES $srcdir/src/hidapi/libusb/hid.c"
+            EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBUSB_CFLAGS"
+            EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBUSB_LDFLAGS"
+        fi
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for hidapi support" >&5
+$as_echo_n "checking for hidapi support... " >&6; }
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hidapi_support" >&5
+$as_echo "$hidapi_support" >&6; }
+    fi
+}
+
 CheckClockGettime()
 {
     # Check whether --enable-clock_gettime was given.
@@ -23907,6 +24066,7 @@
         esac
         CheckTslib
         CheckUSBHID
+        CheckHIDAPI
         CheckPTHREAD
         CheckClockGettime
         CheckLinuxVersion
@@ -23992,6 +24152,18 @@
 
                 SOURCES="$SOURCES $srcdir/src/haptic/android/*.c"
                 have_haptic=yes
+            ;;
+          esac
+        fi
+        # Set up files for the sensor library
+        if test x$enable_sensor = xyes; then
+          case $ARCH in
+            android)
+
+$as_echo "#define SDL_SENSOR_ANDROID 1" >>confdefs.h
+
+                SOURCES="$SOURCES $srcdir/src/sensor/android/*.c"
+                have_sensor=yes
             ;;
           esac
         fi
@@ -24113,10 +24285,11 @@
 
                 SOURCES="$SOURCES $srcdir/src/audio/directsound/*.c"
             fi
-            if test x$have_wasapi = xyes; then
+            if test x$have_wasapi = xyes -a x$enable_wasapi = xyes; then
 
 $as_echo "#define SDL_AUDIO_DRIVER_WASAPI 1" >>confdefs.h
 
+                SUMMARY_audio="${SUMMARY_audio} wasapi"
                 SOURCES="$SOURCES $srcdir/src/audio/wasapi/*.c"
             fi
             have_audio=yes
@@ -24140,7 +24313,13 @@
 $as_echo "#define SDL_JOYSTICK_WINMM 1" >>confdefs.h
 
             fi
+
+$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
+
             SOURCES="$SOURCES $srcdir/src/joystick/windows/*.c"
+            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+            SOURCES="$SOURCES $srcdir/src/hidapi/windows/hid.c"
+            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
             have_joystick=yes
         fi
         if test x$enable_haptic = xyes; then
@@ -24204,7 +24383,7 @@
         else
             LIBUUID=-luuid
         fi
-        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -luser32 -lgdi32 -lwinmm -limm32 -lole32 -loleaut32 -lshell32 -lversion $LIBUUID -static-libgcc"
+        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -luser32 -lgdi32 -lwinmm -limm32 -lole32 -loleaut32 -lshell32 -lsetupapi -lversion $LIBUUID -static-libgcc"
         # The Windows platform requires special setup
         VERSION_SOURCES="$srcdir/src/main/windows/*.rc"
         SDLMAIN_SOURCES="$srcdir/src/main/windows/*.c"
@@ -24461,6 +24640,7 @@
         CheckMETAL
         CheckX11
         CheckMacGL
+        CheckMacGLES
         CheckOpenGLX11
         CheckVulkan
         CheckPTHREAD
@@ -24480,7 +24660,13 @@
 
 $as_echo "#define SDL_JOYSTICK_IOKIT 1" >>confdefs.h
 
+
+$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
+
             SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c"
+            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+            SOURCES="$SOURCES $srcdir/src/hidapi/mac/hid.c"
+            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
             have_joystick=yes
         fi
         # Set up files for the haptic library
@@ -24633,7 +24819,7 @@
 if test x$have_joystick != xyes; then
     if test x$enable_joystick = xyes; then
 
-$as_echo "#define SDL_JOYSTICK_DISABLED 1" >>confdefs.h
+$as_echo "#define SDL_JOYSTICK_DUMMY 1" >>confdefs.h
 
     fi
     SOURCES="$SOURCES $srcdir/src/joystick/dummy/*.c"
@@ -24641,10 +24827,18 @@
 if test x$have_haptic != xyes; then
     if test x$enable_haptic = xyes; then
 
-$as_echo "#define SDL_HAPTIC_DISABLED 1" >>confdefs.h
+$as_echo "#define SDL_HAPTIC_DUMMY 1" >>confdefs.h
 
     fi
     SOURCES="$SOURCES $srcdir/src/haptic/dummy/*.c"
+fi
+if test x$have_sensor != xyes; then
+    if test x$enable_sensor = xyes; then
+
+$as_echo "#define SDL_SENSOR_DUMMY 1" >>confdefs.h
+
+    fi
+    SOURCES="$SOURCES $srcdir/src/sensor/dummy/*.c"
 fi
 if test x$have_threads != xyes; then
     if test x$enable_threads = xyes; then
@@ -24684,54 +24878,27 @@
 SDLTEST_SOURCES="$srcdir/src/test/*.c"
 
 if test x$video_wayland = xyes; then
-    WAYLAND_CORE_PROTOCOL_SOURCE='$(gen)/wayland-protocol.c'
-    WAYLAND_CORE_PROTOCOL_HEADER='$(gen)/wayland-client-protocol.h'
-    WAYLAND_PROTOCOLS_UNSTABLE_SOURCES=`echo $WAYLAND_PROTOCOLS_UNSTABLE |\
-        sed 's,[^ ]\+,\\$(gen)/&-protocol.c,g'`
-    WAYLAND_PROTOCOLS_UNSTABLE_HEADERS=`echo $WAYLAND_PROTOCOLS_UNSTABLE |\
-        sed 's,[^ ]\+,\\$(gen)/&-client-protocol.h,g'`
-    GEN_SOURCES="$GEN_SOURCES $WAYLAND_CORE_PROTOCOL_SOURCE $WAYLAND_PROTOCOLS_UNSTABLE_SOURCES"
-    GEN_HEADERS="$GEN_HEADERS $WAYLAND_CORE_PROTOCOL_HEADER $WAYLAND_PROTOCOLS_UNSTABLE_HEADERS"
+    WAYLAND_PROTOCOLS=`cd $srcdir/wayland-protocols ; for p in *.xml ; do echo -n "\$p" |sed 's,\\.xml\$, ,g' ; done`
+    WAYLAND_PROTOCOLS_SOURCES=`for p in $WAYLAND_PROTOCOLS ; do echo -n "\\$(gen)/\$p-protocol.c " ; done`
+    WAYLAND_PROTOCOLS_HEADERS=`for p in $WAYLAND_PROTOCOLS ; do echo -n "\\$(gen)/\$p-client-protocol.h " ; done`
+    GEN_SOURCES="$GEN_SOURCES $WAYLAND_PROTOCOLS_SOURCES"
+    GEN_HEADERS="$GEN_HEADERS $WAYLAND_PROTOCOLS_HEADERS"
 
-    WAYLAND_CORE_PROTOCOL_SOURCE_DEPENDS="
-$WAYLAND_CORE_PROTOCOL_SOURCE: $WAYLAND_CORE_PROTOCOL_DIR/wayland.xml
-	\$(SHELL) \$(auxdir)/mkinstalldirs \$(gen)
-	\$(RUN_CMD_GEN)\$(WAYLAND_SCANNER) code \$< \$@"
-
-    WAYLAND_CORE_PROTOCOL_HEADER_DEPENDS="
-$WAYLAND_CORE_PROTOCOL_HEADER: $WAYLAND_CORE_PROTOCOL_DIR/wayland.xml
-	\$(SHELL) \$(auxdir)/mkinstalldirs \$(gen)
-	\$(RUN_CMD_GEN)\$(WAYLAND_SCANNER) client-header \$< \$@"
-
-    WAYLAND_CORE_PROTOCOL_OBJECT="
-\$(objects)/`echo $WAYLAND_CORE_PROTOCOL_SOURCE | sed 's/\$(gen)\/\(.*\).c$/\1.lo/'`: $WAYLAND_CORE_PROTOCOL_SOURCE
-	\$(RUN_CMD_CC)\$(LIBTOOL) --tag=CC --mode=compile \$(CC) \$(CFLAGS) \$(EXTRA_CFLAGS) $DEPENDENCY_TRACKING_OPTIONS -c \$< -o \$@"
-
-    WAYLAND_PROTOCOLS_CLIENT_HEADER_UNSTABLE_DEPENDS=`for p in $WAYLAND_PROTOCOLS_UNSTABLE;\
-        do echo ; echo \$p | sed\
-        "s,^\\([a-z\\-]\\+\\)-unstable-\\(v[0-9]\+\\)\$,\\$(gen)/&-client-protocol.h: $WAYLAND_PROTOCOLS_DIR/unstable/\1/&.xml\\\\
-	\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)\\\\
-	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) client-header \\$< \\$@," ; done`
-
-    WAYLAND_PROTOCOLS_CODE_UNSTABLE_DEPENDS=`for p in $WAYLAND_PROTOCOLS_UNSTABLE;\
-        do echo ; echo \$p | sed\
-        "s,^\\([a-z\\-]\\+\\)-unstable-\\(v[0-9]\+\\)\$,\\$(gen)/&-protocol.c: $WAYLAND_PROTOCOLS_DIR/unstable/\1/&.xml\\\\
-	\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)\\\\
-	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) code \\$< \\$@," ; done`
-
-    WAYLAND_PROTOCOLS_OBJECTS_UNSTABLE=`for p in $WAYLAND_PROTOCOLS_UNSTABLE;\
-        do echo ; echo \$p | sed\
-        "s,^\\([a-z\\-]\\+\\)-unstable-\\(v[0-9]\+\\)\$,\\\$(objects)/&-protocol.lo: \\$(gen)/&-protocol.c \\$(gen)/&-client-protocol.h\\\\
-	\\$(RUN_CMD_CC)\\$(LIBTOOL) --tag=CC --mode=compile \\$(CC) \\$(CFLAGS) \\$(EXTRA_CFLAGS) $DEPENDENCY_TRACKING_OPTIONS -c \\$< -o \\$@," ; done`
-
-    WAYLAND_PROTOCOLS_DEPENDS="
-$WAYLAND_CORE_PROTOCOL_SOURCE_DEPENDS
-$WAYLAND_CORE_PROTOCOL_HEADER_DEPENDS
-$WAYLAND_CORE_PROTOCOL_OBJECT
-$WAYLAND_PROTOCOLS_CLIENT_HEADER_UNSTABLE_DEPENDS
-$WAYLAND_PROTOCOLS_CODE_UNSTABLE_DEPENDS
-$WAYLAND_PROTOCOLS_OBJECTS_UNSTABLE
-"
+    WAYLAND_PROTOCOLS_DEPENDS=`for p in $WAYLAND_PROTOCOLS ; do\
+        echo ;\
+        echo "\\$(gen)/\$p-client-protocol.h: \\$(srcdir)/wayland-protocols/\$p.xml" ;\
+        echo "	@\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)" ;\
+        echo "	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) client-header \\$< \\$@" ;\
+        echo ;\
+        echo "\\$(gen)/\$p-protocol.c: \\$(srcdir)/wayland-protocols/\$p.xml" ;\
+        echo "	@\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)" ;\
+        echo "	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) code \\$< \\$@" ;\
+        echo ;\
+        echo "\\$(objects)/\$p-protocol.lo: \\$(gen)/\$p-protocol.c \\$(gen)/\$p-client-protocol.h" ;\
+        echo "	\\$(RUN_CMD_CC)\\$(LIBTOOL) --tag=CC --mode=compile \\$(CC) \\$(CFLAGS) \\$(EXTRA_CFLAGS) $DEPENDENCY_TRACKING_OPTIONS -c \\$< -o \\$@" ;\
+        done ;\
+        echo ;\
+        for s in $WAYLAND_SOURCES ; do echo -n "\$s:" ; for p in $WAYLAND_PROTOCOLS ; do echo -n " \\$(gen)/\$p-client-protocol.h" ; done ; echo ; done ; echo`
 fi
 
 OBJECTS=`echo $SOURCES`
diff --git a/source/configure.in b/source/configure.in
index 1c7e793..ae866ff 100644
--- a/source/configure.in
+++ b/source/configure.in
@@ -20,9 +20,9 @@
 #
 SDL_MAJOR_VERSION=2
 SDL_MINOR_VERSION=0
-SDL_MICRO_VERSION=8
+SDL_MICRO_VERSION=9
 SDL_INTERFACE_AGE=0
-SDL_BINARY_AGE=8
+SDL_BINARY_AGE=9
 SDL_VERSION=$SDL_MAJOR_VERSION.$SDL_MINOR_VERSION.$SDL_MICRO_VERSION
 
 AC_SUBST(SDL_MAJOR_VERSION)
@@ -123,10 +123,17 @@
 #    fi
 #done
 SDL_CFLAGS="$BASE_CFLAGS"
-SDL_LIBS="-lSDL2 $BASE_LDFLAGS"
-CPPFLAGS="$CPPFLAGS $EXTRA_CFLAGS"
-CFLAGS="$CFLAGS $EXTRA_CFLAGS"
-LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS"
+SDL_LIBS="-lSDL2"
+if test "x$BASE_LDFLAGS" != x; then
+    SDL_LIBS="$SDL_LIBS $BASE_LDFLAGS"
+fi
+if test "x$EXTRA_CFLAGS" != x; then
+    CPPFLAGS="$CPPFLAGS $EXTRA_CFLAGS"
+    CFLAGS="$CFLAGS $EXTRA_CFLAGS"
+fi
+if test "x$EXTRA_LDFLAGS" != x; then
+    LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS"
+fi
 
 dnl set this to use on systems that use lib64 instead of lib
 base_libdir=`echo \${libdir} | sed 's/.*\/\(.*\)/\1/; q'`
@@ -271,14 +278,14 @@
     AC_CHECK_FUNCS(malloc calloc realloc free getenv setenv putenv unsetenv qsort abs bcopy memset memcpy memmove wcslen wcscmp strlen strlcpy strlcat _strrev _strupr _strlwr strchr strrchr strstr itoa _ltoa _uitoa _ultoa strtol strtoul _i64toa _ui64toa strtoll strtoull atoi atof strcmp strncmp _stricmp strcasecmp _strnicmp strncasecmp vsscanf vsnprintf fopen64 fseeko fseeko64 sigaction setjmp nanosleep sysconf sysctlbyname getauxval poll)
 
     AC_CHECK_LIB(m, pow, [LIBS="$LIBS -lm"; EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lm"])
-    AC_CHECK_FUNCS(acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf copysign copysignf cos cosf fabs fabsf floor floorf fmod fmodf log logf log10 log10f pow powf scalbn scalbnf sin sinf sqrt sqrtf tan tanf)
+    AC_CHECK_FUNCS(acos acosf asin asinf atan atanf atan2 atan2f ceil ceilf copysign copysignf cos cosf exp expf fabs fabsf floor floorf fmod fmodf log logf log10 log10f pow powf scalbn scalbnf sin sinf sqrt sqrtf tan tanf)
 
     AC_CHECK_LIB(iconv, iconv_open, [LIBS="$LIBS -liconv"; EXTRA_LDFLAGS="$EXTRA_LDFLAGS -liconv"])
     AC_CHECK_FUNCS(iconv)
 
     AC_CHECK_MEMBER(struct sigaction.sa_sigaction,[AC_DEFINE([HAVE_SA_SIGACTION], 1, [ ])], ,[#include <signal.h>])
 
-	dnl Check for additional non-standard headers
+    dnl Check for additional non-standard headers
     AC_CHECK_HEADERS(libunwind.h)
 fi
 
@@ -339,6 +346,7 @@
 #SOURCES="$SOURCES $srcdir/src/filesystem/*.c"
 SOURCES="$SOURCES $srcdir/src/render/*.c"
 SOURCES="$SOURCES $srcdir/src/render/*/*.c"
+SOURCES="$SOURCES $srcdir/src/sensor/*.c"
 SOURCES="$SOURCES $srcdir/src/stdlib/*.c"
 SOURCES="$SOURCES $srcdir/src/thread/*.c"
 SOURCES="$SOURCES $srcdir/src/timer/*.c"
@@ -402,6 +410,14 @@
     AC_DEFINE(SDL_HAPTIC_DISABLED, 1, [ ])
 else
     SUMMARY_modules="${SUMMARY_modules} haptic"
+fi
+AC_ARG_ENABLE(sensor,
+AC_HELP_STRING([--enable-sensor], [Enable the sensor subsystem [[default=yes]]]),
+              , enable_sensor=yes)
+if test x$enable_sensor != xyes; then
+    AC_DEFINE(SDL_SENSOR_DISABLED, 1, [ ])
+else
+    SUMMARY_modules="${SUMMARY_modules} sensor"
 fi
 AC_ARG_ENABLE(power,
 AC_HELP_STRING([--enable-power], [Enable the power subsystem [[default=yes]]]),
@@ -956,7 +972,7 @@
         AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
         AC_MSG_CHECKING(for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support)
         if test x$PKG_CONFIG != xno; then
-        if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
+            if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
                 PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags libpulse-simple`
                 PULSEAUDIO_LIBS=`$PKG_CONFIG --libs libpulse-simple`
                 audio_pulseaudio=yes
@@ -1396,8 +1412,6 @@
                 WAYLAND_CFLAGS=`$PKG_CONFIG --cflags wayland-client wayland-egl wayland-cursor xkbcommon`
                 WAYLAND_LIBS=`$PKG_CONFIG --libs wayland-client wayland-egl wayland-cursor xkbcommon`
                 WAYLAND_SCANNER=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner`
-                WAYLAND_CORE_PROTOCOL_DIR=`$PKG_CONFIG --variable=pkgdatadir wayland-client`
-                WAYLAND_PROTOCOLS_DIR=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`
                 video_wayland=yes
             fi
         fi
@@ -1409,9 +1423,8 @@
                 AC_DEFINE(SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH, 1, [ ])
             fi
 
-            WAYLAND_PROTOCOLS_UNSTABLE="relative-pointer-unstable-v1 pointer-constraints-unstable-v1 xdg-shell-unstable-v6"
-
-            SOURCES="$SOURCES $srcdir/src/video/wayland/*.c"
+            WAYLAND_SOURCES="$srcdir/src/video/wayland/*.c"
+            SOURCES="$SOURCES $WAYLAND_SOURCES"
             EXTRA_CFLAGS="$EXTRA_CFLAGS $WAYLAND_CFLAGS -I\$(gen)"
             AC_ARG_ENABLE(wayland-shared,
 AC_HELP_STRING([--enable-wayland-shared], [dynamically load Wayland support [[default=maybe]]]),
@@ -1744,7 +1757,7 @@
             AC_MSG_CHECKING([for XGenericEvent])
             have_XGenericEvent=no
             AC_TRY_COMPILE([
-              	#include <X11/Xlib.h>
+                #include <X11/Xlib.h>
             ],[
 Display *display;
 XEvent event;
@@ -1858,20 +1871,20 @@
                 SUMMARY_video_x11="${SUMMARY_video_x11} xinput2"
                 AC_DEFINE(SDL_VIDEO_DRIVER_X11_XINPUT2, 1, [ ])
                 AC_MSG_CHECKING(for xinput2 multitouch)
-            	have_xinput2_multitouch=no
-            	AC_TRY_COMPILE([
-              		#include <X11/Xlib.h>
-             		#include <X11/Xproto.h>
-			#include <X11/extensions/XInput2.h>
-            	],[
+                have_xinput2_multitouch=no
+                AC_TRY_COMPILE([
+                    #include <X11/Xlib.h>
+                    #include <X11/Xproto.h>
+                    #include <X11/extensions/XInput2.h>
+                    ],[
 int event_type = XI_TouchBegin;
 XITouchClassInfo *t;
-            	],[
-            	have_xinput2_multitouch=yes
-            	AC_DEFINE([SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH], 1, [])
-                SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
-            	])
-            	AC_MSG_RESULT($have_xinput2_multitouch)
+                    ],[
+                    have_xinput2_multitouch=yes
+                    AC_DEFINE([SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH], 1, [])
+                    SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
+                    ])
+                AC_MSG_RESULT($have_xinput2_multitouch)
             fi
             AC_ARG_ENABLE(video-x11-xrandr,
 AC_HELP_STRING([--enable-video-x11-xrandr], [enable X11 Xrandr extension for fullscreen [[default=yes]]]),
@@ -2150,6 +2163,7 @@
                               , enable_directfb_shared=yes)
 
             AC_DEFINE(SDL_VIDEO_DRIVER_DIRECTFB, 1, [ ])
+            AC_DEFINE(SDL_VIDEO_RENDER_DIRECTFB, 1, [ ])
             SOURCES="$SOURCES $srcdir/src/video/directfb/*.c"
             EXTRA_CFLAGS="$EXTRA_CFLAGS $DIRECTFB_CFLAGS"
 
@@ -2459,6 +2473,19 @@
         AC_DEFINE(SDL_VIDEO_OPENGL_CGL, 1, [ ])
         AC_DEFINE(SDL_VIDEO_RENDER_OGL, 1, [ ])
         SUMMARY_video="${SUMMARY_video} opengl"
+    fi
+}
+
+dnl Check for MacOS OpenGLES
+CheckMacGLES()
+{
+    if test x$enable_video = xyes -a x$enable_video_opengles = xyes; then
+        video_opengl_egl=yes
+        AC_DEFINE(SDL_VIDEO_OPENGL_EGL, 1, [ ])
+        video_opengles_v2=yes
+        AC_DEFINE(SDL_VIDEO_OPENGL_ES2, 1, [ ])
+        AC_DEFINE(SDL_VIDEO_RENDER_OGL_ES2, 1, [ ])
+        SUMMARY_video="${SUMMARY_video} opengl_es2"
     fi
 }
 
@@ -3025,8 +3052,6 @@
         AC_CHECK_HEADER(dinput.h, have_dinput=yes)
         AC_CHECK_HEADER(dxgi.h, have_dxgi=yes)
         AC_CHECK_HEADER(xinput.h, have_xinput=yes)
-        AC_CHECK_HEADER(mmdeviceapi.h, have_wasapi=yes)
-        AC_CHECK_HEADER(audioclient.h,,have_wasapi=no)
         AC_TRY_COMPILE([
 #include <windows.h>
 #include <xinput.h>
@@ -3071,6 +3096,21 @@
             ;;
         esac
     fi
+
+    AC_CHECK_HEADER(mmdeviceapi.h, have_wasapi=yes)
+    if test x$have_wasapi = xyes; then
+        AC_DEFINE(HAVE_MMDEVICEAPI_H,1,[])
+    fi
+    AC_CHECK_HEADER(audioclient.h,,have_wasapi=no)
+    if test x$have_wasapi = xyes; then
+        AC_DEFINE(HAVE_AUDIOCLIENT_H,1,[])
+    fi
+
+    AC_CHECK_HEADER(endpointvolume.h,AC_DEFINE(HAVE_ENDPOINTVOLUME_H,1,[]))
+
+    AC_ARG_ENABLE(wasapi,
+AC_HELP_STRING([--enable-wasapi], [use the Windows WASAPI audio driver [[default=yes]]]),
+                                , enable_wasapi=yes)
 }
 
 dnl Check for the dlfcn.h interface for dynamically loading objects
@@ -3244,6 +3284,41 @@
     esac
 }
 
+dnl Check for HIDAPI joystick drivers
+CheckHIDAPI()
+{
+    # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
+    # so we'll just use libusb when it's available.
+    #
+    # Except that libusb requires root permissions to open devices, so that's not generally useful, and we'll disable this by default.
+    AC_ARG_ENABLE(hidapi,
+AC_HELP_STRING([--enable-hidapi], [use HIDAPI for low level joystick drivers [[default=no]]]),
+                  , enable_hidapi=no)
+    if test x$enable_joystick = xyes -a x$enable_hidapi = xyes; then
+        hidapi_support=no
+        AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+        if test x$PKG_CONFIG != xno; then
+            LIBUSB_CFLAGS=`$PKG_CONFIG --cflags libusb-1.0`
+            LIBUSB_LDFLAGS=`$PKG_CONFIG --libs libusb-1.0`
+            save_CFLAGS="$CFLAGS"
+            CFLAGS="$save_CFLAGS $LIBUSB_CFLAGS"
+            AC_CHECK_HEADER(libusb.h, have_libusb_h=yes)
+            CFLAGS="$save_CFLAGS"
+        fi
+        if test x$have_libusb_h = xyes; then
+            hidapi_support=yes
+            AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
+            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
+            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+            SOURCES="$SOURCES $srcdir/src/hidapi/libusb/hid.c"
+            EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBUSB_CFLAGS"
+            EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBUSB_LDFLAGS"
+        fi
+        AC_MSG_CHECKING(for hidapi support)
+        AC_MSG_RESULT($hidapi_support)
+    fi
+}
+
 dnl Check for clock_gettime()
 CheckClockGettime()
 {
@@ -3365,6 +3440,7 @@
         esac
         CheckTslib
         CheckUSBHID
+        CheckHIDAPI
         CheckPTHREAD
         CheckClockGettime
         CheckLinuxVersion
@@ -3434,6 +3510,16 @@
                 AC_DEFINE(SDL_HAPTIC_ANDROID, 1, [ ])
                 SOURCES="$SOURCES $srcdir/src/haptic/android/*.c"
                 have_haptic=yes
+            ;;
+          esac
+        fi
+        # Set up files for the sensor library
+        if test x$enable_sensor = xyes; then
+          case $ARCH in
+            android)
+                AC_DEFINE(SDL_SENSOR_ANDROID, 1, [ ])
+                SOURCES="$SOURCES $srcdir/src/sensor/android/*.c"
+                have_sensor=yes
             ;;
           esac
         fi
@@ -3531,8 +3617,9 @@
                 AC_DEFINE(SDL_AUDIO_DRIVER_DSOUND, 1, [ ])
                 SOURCES="$SOURCES $srcdir/src/audio/directsound/*.c"
             fi
-            if test x$have_wasapi = xyes; then
+            if test x$have_wasapi = xyes -a x$enable_wasapi = xyes; then
                 AC_DEFINE(SDL_AUDIO_DRIVER_WASAPI, 1, [ ])
+                SUMMARY_audio="${SUMMARY_audio} wasapi"
                 SOURCES="$SOURCES $srcdir/src/audio/wasapi/*.c"
             fi
             have_audio=yes
@@ -3550,7 +3637,11 @@
             else
                 AC_DEFINE(SDL_JOYSTICK_WINMM, 1, [ ])
             fi
+            AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
             SOURCES="$SOURCES $srcdir/src/joystick/windows/*.c"
+            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+            SOURCES="$SOURCES $srcdir/src/hidapi/windows/hid.c"
+            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
             have_joystick=yes
         fi
         if test x$enable_haptic = xyes; then
@@ -3600,7 +3691,7 @@
         else
             LIBUUID=-luuid
         fi
-        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -luser32 -lgdi32 -lwinmm -limm32 -lole32 -loleaut32 -lshell32 -lversion $LIBUUID -static-libgcc"
+        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -luser32 -lgdi32 -lwinmm -limm32 -lole32 -loleaut32 -lshell32 -lsetupapi -lversion $LIBUUID -static-libgcc"
         # The Windows platform requires special setup
         VERSION_SOURCES="$srcdir/src/main/windows/*.rc"
         SDLMAIN_SOURCES="$srcdir/src/main/windows/*.c"
@@ -3773,6 +3864,7 @@
         CheckMETAL
         CheckX11
         CheckMacGL
+        CheckMacGLES
         CheckOpenGLX11
         CheckVulkan
         CheckPTHREAD
@@ -3788,7 +3880,11 @@
         # Set up files for the joystick library
         if test x$enable_joystick = xyes; then
             AC_DEFINE(SDL_JOYSTICK_IOKIT, 1, [ ])
+            AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
             SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c"
+            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+            SOURCES="$SOURCES $srcdir/src/hidapi/mac/hid.c"
+            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
             have_joystick=yes
         fi
         # Set up files for the haptic library
@@ -3916,15 +4012,21 @@
 
 if test x$have_joystick != xyes; then
     if test x$enable_joystick = xyes; then
-        AC_DEFINE(SDL_JOYSTICK_DISABLED, 1, [ ])
+        AC_DEFINE(SDL_JOYSTICK_DUMMY, 1, [ ])
     fi
     SOURCES="$SOURCES $srcdir/src/joystick/dummy/*.c"
 fi
 if test x$have_haptic != xyes; then
     if test x$enable_haptic = xyes; then
-        AC_DEFINE(SDL_HAPTIC_DISABLED, 1, [ ])
+        AC_DEFINE(SDL_HAPTIC_DUMMY, 1, [ ])
     fi
     SOURCES="$SOURCES $srcdir/src/haptic/dummy/*.c"
+fi
+if test x$have_sensor != xyes; then
+    if test x$enable_sensor = xyes; then
+        AC_DEFINE(SDL_SENSOR_DUMMY, 1, [ ])
+    fi
+    SOURCES="$SOURCES $srcdir/src/sensor/dummy/*.c"
 fi
 if test x$have_threads != xyes; then
     if test x$enable_threads = xyes; then
@@ -3956,54 +4058,27 @@
 SDLTEST_SOURCES="$srcdir/src/test/*.c"
 
 if test x$video_wayland = xyes; then
-    WAYLAND_CORE_PROTOCOL_SOURCE='$(gen)/wayland-protocol.c'
-    WAYLAND_CORE_PROTOCOL_HEADER='$(gen)/wayland-client-protocol.h'
-    WAYLAND_PROTOCOLS_UNSTABLE_SOURCES=`echo $WAYLAND_PROTOCOLS_UNSTABLE |\
-        sed 's,[[^ ]]\+,\\$(gen)/&-protocol.c,g'`
-    WAYLAND_PROTOCOLS_UNSTABLE_HEADERS=`echo $WAYLAND_PROTOCOLS_UNSTABLE |\
-        sed 's,[[^ ]]\+,\\$(gen)/&-client-protocol.h,g'`
-    GEN_SOURCES="$GEN_SOURCES $WAYLAND_CORE_PROTOCOL_SOURCE $WAYLAND_PROTOCOLS_UNSTABLE_SOURCES"
-    GEN_HEADERS="$GEN_HEADERS $WAYLAND_CORE_PROTOCOL_HEADER $WAYLAND_PROTOCOLS_UNSTABLE_HEADERS"
+    WAYLAND_PROTOCOLS=`cd $srcdir/wayland-protocols ; for p in *.xml ; do echo -n "\$p" |sed 's,\\.xml\$, ,g' ; done`
+    WAYLAND_PROTOCOLS_SOURCES=`for p in $WAYLAND_PROTOCOLS ; do echo -n "\\$(gen)/\$p-protocol.c " ; done`
+    WAYLAND_PROTOCOLS_HEADERS=`for p in $WAYLAND_PROTOCOLS ; do echo -n "\\$(gen)/\$p-client-protocol.h " ; done`
+    GEN_SOURCES="$GEN_SOURCES $WAYLAND_PROTOCOLS_SOURCES"
+    GEN_HEADERS="$GEN_HEADERS $WAYLAND_PROTOCOLS_HEADERS"
 
-    WAYLAND_CORE_PROTOCOL_SOURCE_DEPENDS="
-$WAYLAND_CORE_PROTOCOL_SOURCE: $WAYLAND_CORE_PROTOCOL_DIR/wayland.xml
-	\$(SHELL) \$(auxdir)/mkinstalldirs \$(gen)
-	\$(RUN_CMD_GEN)\$(WAYLAND_SCANNER) code \$< \$@"
-
-    WAYLAND_CORE_PROTOCOL_HEADER_DEPENDS="
-$WAYLAND_CORE_PROTOCOL_HEADER: $WAYLAND_CORE_PROTOCOL_DIR/wayland.xml
-	\$(SHELL) \$(auxdir)/mkinstalldirs \$(gen)
-	\$(RUN_CMD_GEN)\$(WAYLAND_SCANNER) client-header \$< \$@"
-
-    WAYLAND_CORE_PROTOCOL_OBJECT="
-\$(objects)/`echo $WAYLAND_CORE_PROTOCOL_SOURCE | sed 's/\$(gen)\/\(.*\).c$/\1.lo/'`: $WAYLAND_CORE_PROTOCOL_SOURCE
-	\$(RUN_CMD_CC)\$(LIBTOOL) --tag=CC --mode=compile \$(CC) \$(CFLAGS) \$(EXTRA_CFLAGS) $DEPENDENCY_TRACKING_OPTIONS -c \$< -o \$@"
-
-    WAYLAND_PROTOCOLS_CLIENT_HEADER_UNSTABLE_DEPENDS=`for p in $WAYLAND_PROTOCOLS_UNSTABLE;\
-        do echo ; echo \$p | sed\
-        "s,^\\([[a-z\\-]]\\+\\)-unstable-\\(v[[0-9]]\+\\)\$,\\$(gen)/&-client-protocol.h: $WAYLAND_PROTOCOLS_DIR/unstable/\1/&.xml\\\\
-	\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)\\\\
-	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) client-header \\$< \\$@," ; done`
-
-    WAYLAND_PROTOCOLS_CODE_UNSTABLE_DEPENDS=`for p in $WAYLAND_PROTOCOLS_UNSTABLE;\
-        do echo ; echo \$p | sed\
-        "s,^\\([[a-z\\-]]\\+\\)-unstable-\\(v[[0-9]]\+\\)\$,\\$(gen)/&-protocol.c: $WAYLAND_PROTOCOLS_DIR/unstable/\1/&.xml\\\\
-	\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)\\\\
-	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) code \\$< \\$@," ; done`
-
-    WAYLAND_PROTOCOLS_OBJECTS_UNSTABLE=`for p in $WAYLAND_PROTOCOLS_UNSTABLE;\
-        do echo ; echo \$p | sed\
-        "s,^\\([[a-z\\-]]\\+\\)-unstable-\\(v[[0-9]]\+\\)\$,\\\$(objects)/&-protocol.lo: \\$(gen)/&-protocol.c \\$(gen)/&-client-protocol.h\\\\
-	\\$(RUN_CMD_CC)\\$(LIBTOOL) --tag=CC --mode=compile \\$(CC) \\$(CFLAGS) \\$(EXTRA_CFLAGS) $DEPENDENCY_TRACKING_OPTIONS -c \\$< -o \\$@," ; done`
-
-    WAYLAND_PROTOCOLS_DEPENDS="
-$WAYLAND_CORE_PROTOCOL_SOURCE_DEPENDS
-$WAYLAND_CORE_PROTOCOL_HEADER_DEPENDS
-$WAYLAND_CORE_PROTOCOL_OBJECT
-$WAYLAND_PROTOCOLS_CLIENT_HEADER_UNSTABLE_DEPENDS
-$WAYLAND_PROTOCOLS_CODE_UNSTABLE_DEPENDS
-$WAYLAND_PROTOCOLS_OBJECTS_UNSTABLE
-"
+    WAYLAND_PROTOCOLS_DEPENDS=`for p in $WAYLAND_PROTOCOLS ; do\
+        echo ;\
+        echo "\\$(gen)/\$p-client-protocol.h: \\$(srcdir)/wayland-protocols/\$p.xml" ;\
+        echo "	@\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)" ;\
+        echo "	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) client-header \\$< \\$@" ;\
+        echo ;\
+        echo "\\$(gen)/\$p-protocol.c: \\$(srcdir)/wayland-protocols/\$p.xml" ;\
+        echo "	@\\$(SHELL) \\$(auxdir)/mkinstalldirs \\$(gen)" ;\
+        echo "	\\$(RUN_CMD_GEN)\\$(WAYLAND_SCANNER) code \\$< \\$@" ;\
+        echo ;\
+        echo "\\$(objects)/\$p-protocol.lo: \\$(gen)/\$p-protocol.c \\$(gen)/\$p-client-protocol.h" ;\
+        echo "	\\$(RUN_CMD_CC)\\$(LIBTOOL) --tag=CC --mode=compile \\$(CC) \\$(CFLAGS) \\$(EXTRA_CFLAGS) $DEPENDENCY_TRACKING_OPTIONS -c \\$< -o \\$@" ;\
+        done ;\
+        echo ;\
+        for s in $WAYLAND_SOURCES ; do echo -n "\$s:" ; for p in $WAYLAND_PROTOCOLS ; do echo -n " \\$(gen)/\$p-client-protocol.h" ; done ; echo ; done ; echo`
 fi
 
 OBJECTS=`echo $SOURCES`
diff --git a/source/debian/changelog b/source/debian/changelog
index 54f4397..ba4cf4a 100644
--- a/source/debian/changelog
+++ b/source/debian/changelog
@@ -1,3 +1,9 @@
+libsdl2 (2.0.9) UNRELEASED; urgency=low
+
+  * Updated SDL to version 2.0.9
+
+ -- Sam Lantinga <slouken@libsdl.org>  Wed, 26 Sep 2018 10:02:21 -0800
+
 libsdl2 (2.0.8) UNRELEASED; urgency=low
 
   * Updated SDL to version 2.0.8
diff --git a/source/docs/README-android.md b/source/docs/README-android.md
index 2d20534..0aa90e7 100644
--- a/source/docs/README-android.md
+++ b/source/docs/README-android.md
@@ -14,10 +14,10 @@
  Requirements
 ================================================================================
 
-Android SDK (version 19 or later)
+Android SDK (version 26 or later)
 https://developer.android.com/sdk/index.html
 
-Android NDK r10e or later
+Android NDK r15c or later
 https://developer.android.com/tools/sdk/ndk/index.html
 
 Minimum API level supported by SDL: 14 (Android 4.0.1)
@@ -77,18 +77,16 @@
    and rename it to the name of your project.
 2. Move or symlink this SDL directory into the "<project>/app/jni" directory
 3. Edit "<project>/app/jni/src/Android.mk" to include your source files
-4. Run 'ndk-build' (a script provided by the NDK). This compiles the C source
 
-If you want to use Android Studio (recommended), skip to the Android Studio section below.
+4a. If you want to use Android Studio, simply open your <project> directory and start building.
 
-5. Run './gradlew installDebug' in the project directory. This compiles the .java, creates an .apk with the native code embedded, and installs it on any connected Android device
+4b. If you want to build manually, run './gradlew installDebug' in the project directory. This compiles the .java, creates an .apk with the native code embedded, and installs it on any connected Android device
 
 Here's an explanation of the files in the Android project, so you can customize them:
 
     android-project/app
         build.gradle            - build info including the application version and SDK
-        src/main/AndroidManifest.xml	- package manifest. Among others, it contains the class name
-        			  of the main Activity and the package name of the application.
+        src/main/AndroidManifest.xml	- package manifest. Among others, it contains the class name of the main Activity and the package name of the application.
         jni/			- directory holding native code
         jni/Application.mk	- Application JNI settings, including target platform and STL library
         jni/Android.mk		- Android makefile that can call recursively the Android.mk files in all subdirectories
@@ -216,26 +214,10 @@
 You can use STL in your project by creating an Application.mk file in the jni
 folder and adding the following line:
 
-    APP_STL := stlport_static
+    APP_STL := c++_shared
 
-For more information check out CPLUSPLUS-SUPPORT.html in the NDK documentation.
-
-
-================================================================================
- Additional documentation
-================================================================================
-
-The documentation in the NDK docs directory is very helpful in understanding the
-build process and how to work with native code on the Android platform.
-
-The best place to start is with docs/OVERVIEW.TXT
-
-
-================================================================================
- Using Android Studio
-================================================================================
-
-You can open your project directory with Android Studio and run it normally.
+For more information go here:
+	https://developer.android.com/ndk/guides/cpp-support
 
 
 ================================================================================
@@ -291,7 +273,10 @@
 
     ndk-build V=1
 
-If your application crashes in native code, you can use addr2line to convert the
+If your application crashes in native code, you can use ndk-stack to get a symbolic stack trace:
+	https://developer.android.com/ndk/guides/ndk-stack
+
+If you want to go through the process manually, you can use addr2line to convert the
 addresses in the stack trace to lines in your code.
 
 For example, if your crash looks like this:
diff --git a/source/docs/README-raspberrypi.md b/source/docs/README-raspberrypi.md
index fbcffa1..5e23ad5 100644
--- a/source/docs/README-raspberrypi.md
+++ b/source/docs/README-raspberrypi.md
@@ -27,6 +27,16 @@
     
 sudo apt-get install libraspberrypi0 libraspberrypi-bin libraspberrypi-dev
 
+
+================================================================================
+ NEON
+================================================================================
+
+If your Pi has NEON support, make sure you add -mfpu=neon to your CFLAGS so
+that SDL will select some otherwise-disabled highly-optimized code. The
+original Pi units don't have NEON, the Pi2 probably does, and the Pi3
+definitely does.
+
 ================================================================================
  Cross compiling from x86 Linux
 ================================================================================
diff --git a/source/include/SDL.h b/source/include/SDL.h
index d48d9d4..fc35a41 100644
--- a/source/include/SDL.h
+++ b/source/include/SDL.h
@@ -51,6 +51,7 @@
 #include "SDL_power.h"
 #include "SDL_render.h"
 #include "SDL_rwops.h"
+#include "SDL_sensor.h"
 #include "SDL_shape.h"
 #include "SDL_system.h"
 #include "SDL_thread.h"
@@ -80,10 +81,11 @@
 #define SDL_INIT_HAPTIC         0x00001000u
 #define SDL_INIT_GAMECONTROLLER 0x00002000u  /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
 #define SDL_INIT_EVENTS         0x00004000u
+#define SDL_INIT_SENSOR         0x00008000u
 #define SDL_INIT_NOPARACHUTE    0x00100000u  /**< compatibility; this flag is ignored. */
 #define SDL_INIT_EVERYTHING ( \
                 SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \
-                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \
+                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR \
             )
 /* @} */
 
diff --git a/source/include/SDL_audio.h b/source/include/SDL_audio.h
index d6ea689..d3e1bfa 100644
--- a/source/include/SDL_audio.h
+++ b/source/include/SDL_audio.h
@@ -140,7 +140,8 @@
 #define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE    0x00000001
 #define SDL_AUDIO_ALLOW_FORMAT_CHANGE       0x00000002
 #define SDL_AUDIO_ALLOW_CHANNELS_CHANGE     0x00000004
-#define SDL_AUDIO_ALLOW_ANY_CHANGE          (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
+#define SDL_AUDIO_ALLOW_SAMPLES_CHANGE      0x00000008
+#define SDL_AUDIO_ALLOW_ANY_CHANGE          (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
 /* @} */
 
 /* @} *//* Audio flags */
diff --git a/source/include/SDL_config.h b/source/include/SDL_config.h
index 7e0340c..32f4113 100644
--- a/source/include/SDL_config.h
+++ b/source/include/SDL_config.h
@@ -41,8 +41,10 @@
 #include "SDL_config_android.h"
 #elif defined(__PSP__)
 #include "SDL_config_psp.h"
+#elif defined(__OS2__)
+#include "SDL_config_os2.h"
 #else
-/* This is a minimal configuration just to get SDL running on new platforms */
+/* This is a minimal configuration just to get SDL running on new platforms. */
 #include "SDL_config_minimal.h"
 #endif /* platform config */
 
diff --git a/source/include/SDL_config.h.cmake b/source/include/SDL_config.h.cmake
index a8d230c..48dd2d4 100644
--- a/source/include/SDL_config.h.cmake
+++ b/source/include/SDL_config.h.cmake
@@ -144,6 +144,8 @@
 #cmakedefine HAVE_COPYSIGNF 1
 #cmakedefine HAVE_COS 1
 #cmakedefine HAVE_COSF 1
+#cmakedefine HAVE_EXP 1
+#cmakedefine HAVE_EXPF 1
 #cmakedefine HAVE_FABS 1
 #cmakedefine HAVE_FABSF 1
 #cmakedefine HAVE_FLOOR 1
@@ -207,6 +209,11 @@
 #cmakedefine HAVE_DINPUT_H @HAVE_DINPUT_H@
 #cmakedefine HAVE_XINPUT_H @HAVE_XINPUT_H@
 #cmakedefine HAVE_DXGI_H @HAVE_DXGI_H@
+
+#cmakedefine HAVE_ENDPOINTVOLUME_H @HAVE_ENDPOINTVOLUME_H@
+#cmakedefine HAVE_MMDEVICEAPI_H @HAVE_MMDEVICEAPI_H@
+#cmakedefine HAVE_AUDIOCLIENT_H @HAVE_AUDIOCLIENT_H@
+
 #cmakedefine HAVE_XINPUT_GAMEPAD_EX @HAVE_XINPUT_GAMEPAD_EX@
 #cmakedefine HAVE_XINPUT_STATE_EX @HAVE_XINPUT_STATE_EX@
 
@@ -221,6 +228,7 @@
 #cmakedefine SDL_FILE_DISABLED @SDL_FILE_DISABLED@
 #cmakedefine SDL_JOYSTICK_DISABLED @SDL_JOYSTICK_DISABLED@
 #cmakedefine SDL_HAPTIC_DISABLED @SDL_HAPTIC_DISABLED@
+#cmakedefine SDL_SENSOR_DISABLED @SDL_SENSOR_DISABLED@
 #cmakedefine SDL_LOADSO_DISABLED @SDL_LOADSO_DISABLED@
 #cmakedefine SDL_RENDER_DISABLED @SDL_RENDER_DISABLED@
 #cmakedefine SDL_THREADS_DISABLED @SDL_THREADS_DISABLED@
@@ -285,6 +293,10 @@
 #cmakedefine SDL_HAPTIC_XINPUT @SDL_HAPTIC_XINPUT@
 #cmakedefine SDL_HAPTIC_ANDROID @SDL_HAPTIC_ANDROID@
 
+/* Enable various sensor drivers */
+#cmakedefine SDL_SENSOR_ANDROID @SDL_SENSOR_ANDROID@
+#cmakedefine SDL_SENSOR_DUMMY @SDL_SENSOR_DUMMY@
+
 /* Enable various shared object loading systems */
 #cmakedefine SDL_LOADSO_DLOPEN @SDL_LOADSO_DLOPEN@
 #cmakedefine SDL_LOADSO_DUMMY @SDL_LOADSO_DUMMY@
diff --git a/source/include/SDL_config.h.in b/source/include/SDL_config.h.in
index 422f47f..883b6f4 100644
--- a/source/include/SDL_config.h.in
+++ b/source/include/SDL_config.h.in
@@ -149,6 +149,8 @@
 #undef HAVE_COPYSIGNF
 #undef HAVE_COS
 #undef HAVE_COSF
+#undef HAVE_EXP
+#undef HAVE_EXPF
 #undef HAVE_FABS
 #undef HAVE_FABSF
 #undef HAVE_FLOOR
@@ -207,6 +209,9 @@
 #undef HAVE_DSOUND_H
 #undef HAVE_DXGI_H
 #undef HAVE_XINPUT_H
+#undef HAVE_ENDPOINTVOLUME_H
+#undef HAVE_MMDEVICEAPI_H
+#undef HAVE_AUDIOCLIENT_H
 #undef HAVE_XINPUT_GAMEPAD_EX
 #undef HAVE_XINPUT_STATE_EX
 
@@ -221,6 +226,7 @@
 #undef SDL_FILE_DISABLED
 #undef SDL_JOYSTICK_DISABLED
 #undef SDL_HAPTIC_DISABLED
+#undef SDL_SENSOR_DISABLED
 #undef SDL_LOADSO_DISABLED
 #undef SDL_RENDER_DISABLED
 #undef SDL_THREADS_DISABLED
@@ -277,6 +283,7 @@
 #undef SDL_JOYSTICK_WINMM
 #undef SDL_JOYSTICK_USBHID
 #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
+#undef SDL_JOYSTICK_HIDAPI
 #undef SDL_JOYSTICK_EMSCRIPTEN
 #undef SDL_HAPTIC_DUMMY
 #undef SDL_HAPTIC_ANDROID
@@ -285,6 +292,10 @@
 #undef SDL_HAPTIC_DINPUT
 #undef SDL_HAPTIC_XINPUT
 
+/* Enable various sensor drivers */
+#undef SDL_SENSOR_ANDROID
+#undef SDL_SENSOR_DUMMY
+
 /* Enable various shared object loading systems */
 #undef SDL_LOADSO_DLOPEN
 #undef SDL_LOADSO_DUMMY
diff --git a/source/include/SDL_config_android.h b/source/include/SDL_config_android.h
index 4c4da37..f2b28cf 100644
--- a/source/include/SDL_config_android.h
+++ b/source/include/SDL_config_android.h
@@ -98,6 +98,8 @@
 #define HAVE_COPYSIGNF  1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -132,8 +134,12 @@
 
 /* Enable various input drivers */
 #define SDL_JOYSTICK_ANDROID    1
+#define SDL_JOYSTICK_HIDAPI    1
 #define SDL_HAPTIC_ANDROID    1
 
+/* Enable sensor driver */
+#define SDL_SENSOR_ANDROID  1
+
 /* Enable various shared object loading systems */
 #define SDL_LOADSO_DLOPEN   1
 
diff --git a/source/include/SDL_config_iphoneos.h b/source/include/SDL_config_iphoneos.h
index 7b0a6ca..56e2b43 100644
--- a/source/include/SDL_config_iphoneos.h
+++ b/source/include/SDL_config_iphoneos.h
@@ -99,6 +99,8 @@
 #define HAVE_COPYSIGNF  1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -135,6 +137,14 @@
 
 /* Enable MFi joystick support */
 #define SDL_JOYSTICK_MFI 1
+/*#define SDL_JOYSTICK_HIDAPI 1*/
+
+#ifdef __TVOS__
+#define SDL_SENSOR_DUMMY    1
+#else
+/* Enable the CoreMotion sensor driver */
+#define SDL_SENSOR_COREMOTION   1
+#endif
 
 /* Enable Unix style SO loading */
 #define SDL_LOADSO_DLOPEN 1
diff --git a/source/include/SDL_config_macosx.h b/source/include/SDL_config_macosx.h
index 29f583e..9ebd4a3 100644
--- a/source/include/SDL_config_macosx.h
+++ b/source/include/SDL_config_macosx.h
@@ -102,6 +102,8 @@
 #define HAVE_COPYSIGNF  1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -135,8 +137,12 @@
 
 /* Enable various input drivers */
 #define SDL_JOYSTICK_IOKIT  1
+#define SDL_JOYSTICK_HIDAPI  1
 #define SDL_HAPTIC_IOKIT    1
 
+/* Enable the dummy sensor driver */
+#define SDL_SENSOR_DUMMY  1
+
 /* Enable various shared object loading systems */
 #define SDL_LOADSO_DLOPEN   1
 
diff --git a/source/include/SDL_config_minimal.h b/source/include/SDL_config_minimal.h
index 5b03d8b..bf7fc44 100644
--- a/source/include/SDL_config_minimal.h
+++ b/source/include/SDL_config_minimal.h
@@ -64,6 +64,9 @@
 /* Enable the stub haptic driver (src/haptic/dummy/\*.c) */
 #define SDL_HAPTIC_DISABLED 1
 
+/* Enable the stub sensor driver (src/sensor/dummy/\*.c) */
+#define SDL_SENSOR_DISABLED 1
+
 /* Enable the stub shared object loader (src/loadso/dummy/\*.c) */
 #define SDL_LOADSO_DISABLED 1
 
diff --git a/source/include/SDL_config_os2.h b/source/include/SDL_config_os2.h
new file mode 100644
index 0000000..d1e4bb2
--- /dev/null
+++ b/source/include/SDL_config_os2.h
@@ -0,0 +1,170 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_config_os2_h_
+#define SDL_config_os2_h_
+#define SDL_config_h_
+
+#include "SDL_platform.h"
+
+#define SDL_AUDIO_DRIVER_DUMMY 1
+#define SDL_AUDIO_DRIVER_DISK 1
+
+#define SDL_POWER_DISABLED  1
+#define SDL_JOYSTICK_DISABLED 1
+#define SDL_HAPTIC_DISABLED 1
+/*#undef SDL_JOYSTICK_HIDAPI */
+
+#define SDL_SENSOR_DUMMY 1
+#define SDL_VIDEO_DRIVER_DUMMY 1
+
+/* Enable OpenGL support */
+/* #undef SDL_VIDEO_OPENGL */
+
+/* Enable Vulkan support */
+/* #undef SDL_VIDEO_VULKAN */
+
+#define SDL_LOADSO_DISABLED 1
+#define SDL_THREADS_DISABLED 1
+#define SDL_TIMERS_DISABLED 1
+#define SDL_FILESYSTEM_DUMMY 1
+
+/* Enable assembly routines */
+#define SDL_ASSEMBLY_ROUTINES 1
+
+/* #undef HAVE_LIBSAMPLERATE_H */
+
+/* Enable dynamic libsamplerate support */
+/* #undef SDL_LIBSAMPLERATE_DYNAMIC */
+
+#define HAVE_LIBC 1
+
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_STDIO_H 1
+#define STDC_HEADERS 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STDARG_H 1
+#define HAVE_STDDEF_H 1
+#define HAVE_MALLOC_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRINGS_H 1
+#define HAVE_WCHAR_H 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_CTYPE_H 1
+#define HAVE_MATH_H 1
+#define HAVE_FLOAT_H 1
+#define HAVE_SIGNAL_H 1
+
+#define HAVE_MALLOC 1
+#define HAVE_CALLOC 1
+#define HAVE_REALLOC 1
+#define HAVE_FREE 1
+#if defined(__WATCOMC__)
+#define HAVE__FSEEKI64 1
+#define HAVE__FTELLI64 1
+#endif
+#define HAVE_ALLOCA 1
+#define HAVE_GETENV 1
+#define HAVE_SETENV 1
+#define HAVE_PUTENV 1
+#define HAVE_QSORT 1
+#define HAVE_ABS 1
+#define HAVE_BCOPY 1
+#define HAVE_MEMSET 1
+#define HAVE_MEMCPY 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MEMCMP 1
+#define HAVE_WCSLEN 1
+#define HAVE_WCSLCPY 1
+#define HAVE_WCSLCAT 1
+#define HAVE_WCSCMP 1
+#define HAVE_STRLEN 1
+#define HAVE_STRLCPY 1
+#define HAVE_STRLCAT 1
+#define HAVE__STRREV 1
+#define HAVE__STRUPR 1
+#define HAVE__STRLWR 1
+#define HAVE_INDEX 1
+#define HAVE_RINDEX 1
+#define HAVE_STRCHR 1
+#define HAVE_STRRCHR 1
+#define HAVE_STRSTR 1
+#define HAVE_ITOA 1
+#define HAVE__LTOA 1
+#define HAVE__ULTOA 1
+#define HAVE_STRTOL 1
+#define HAVE_STRTOUL 1
+#define HAVE__I64TOA 1
+#define HAVE__UI64TOA 1
+#define HAVE_STRTOLL 1
+#define HAVE_STRTOULL 1
+#define HAVE_STRTOD 1
+#define HAVE_ATOI 1
+#define HAVE_ATOF 1
+#define HAVE_STRCMP 1
+#define HAVE_STRNCMP 1
+#define HAVE_STRICMP 1
+#define HAVE_STRCASECMP 1
+#define HAVE_STRNCASECMP 1
+#define HAVE_SSCANF 1
+#define HAVE_SNPRINTF 1
+#define HAVE_VSNPRINTF 1
+#define HAVE_SETJMP 1
+#define HAVE_ACOS 1
+/* #undef HAVE_ACOSF */
+#define HAVE_ASIN 1
+/* #undef HAVE_ASINF */
+#define HAVE_ATAN 1
+#define HAVE_ATAN2 1
+/* #undef HAVE_ATAN2F */
+#define HAVE_CEIL 1
+/* #undef HAVE_CEILF */
+/* #undef HAVE_COPYSIGN */
+/* #undef HAVE_COPYSIGNF */
+#define HAVE_COS 1
+/* #undef HAVE_COSF */
+#define HAVE_EXP 1
+/* #undef HAVE_EXPF */
+#define HAVE_FABS 1
+/* #undef HAVE_FABSF */
+#define HAVE_FLOOR 1
+/* #undef HAVE_FLOORF */
+#define HAVE_FMOD 1
+/* #undef HAVE_FMODF */
+#define HAVE_LOG 1
+/* #undef HAVE_LOGF */
+#define HAVE_LOG10 1
+/* #undef HAVE_LOG10F */
+#define HAVE_POW 1
+/* #undef HAVE_POWF */
+#define HAVE_SIN 1
+/* #undef HAVE_SINF */
+/* #undef HAVE_SCALBN */
+/* #undef HAVE_SCALBNF */
+#define HAVE_SQRT 1
+/* #undef HAVE_SQRTF */
+#define HAVE_TAN 1
+/* #undef HAVE_TANF */
+
+#endif /* SDL_config_os2_h_ */
diff --git a/source/include/SDL_config_pandora.h b/source/include/SDL_config_pandora.h
index be5a85c..64111a1 100644
--- a/source/include/SDL_config_pandora.h
+++ b/source/include/SDL_config_pandora.h
@@ -90,6 +90,7 @@
 #define HAVE_COPYSIGN 1
 #define HAVE_COS 1
 #define HAVE_COSF 1
+#define HAVE_EXP 1
 #define HAVE_FABS 1
 #define HAVE_FLOOR 1
 #define HAVE_LOG 1
@@ -113,6 +114,8 @@
 #define SDL_JOYSTICK_LINUX 1
 #define SDL_HAPTIC_LINUX 1
 
+#define SDL_SENSOR_DUMMY 1
+
 #define SDL_LOADSO_DLOPEN 1
 
 #define SDL_THREAD_PTHREAD 1
diff --git a/source/include/SDL_config_psp.h b/source/include/SDL_config_psp.h
index 61c3349..2422672 100644
--- a/source/include/SDL_config_psp.h
+++ b/source/include/SDL_config_psp.h
@@ -97,6 +97,8 @@
 #define HAVE_COPYSIGNF  1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -126,22 +128,25 @@
 /* PSP isn't that sophisticated */
 #define LACKS_SYS_MMAN_H 1
 
-/* Enable the stub thread support (src/thread/psp/\*.c) */
+/* Enable the PSP thread support (src/thread/psp/\*.c) */
 #define SDL_THREAD_PSP  1
 
-/* Enable the stub timer support (src/timer/psp/\*.c) */
+/* Enable the PSP timer support (src/timer/psp/\*.c) */
 #define SDL_TIMERS_PSP  1
 
-/* Enable the stub joystick driver (src/joystick/psp/\*.c) */
+/* Enable the PSP joystick driver (src/joystick/psp/\*.c) */
 #define SDL_JOYSTICK_PSP        1
 
-/* Enable the stub audio driver (src/audio/psp/\*.c) */
+/* Enable the dummy sensor driver */
+#define SDL_SENSOR_DUMMY  1
+
+/* Enable the PSP audio driver (src/audio/psp/\*.c) */
 #define SDL_AUDIO_DRIVER_PSP    1
 
-/* PSP video dirver */
+/* PSP video driver */
 #define SDL_VIDEO_DRIVER_PSP   1
 
-/* PSP render dirver */
+/* PSP render driver */
 #define SDL_VIDEO_RENDER_PSP   1
 
 #define SDL_POWER_PSP          1
diff --git a/source/include/SDL_config_windows.h b/source/include/SDL_config_windows.h
index 52a9ece..c58be8e 100644
--- a/source/include/SDL_config_windows.h
+++ b/source/include/SDL_config_windows.h
@@ -82,6 +82,9 @@
 #define HAVE_DSOUND_H 1
 #define HAVE_DXGI_H 1
 #define HAVE_XINPUT_H 1
+#define HAVE_MMDEVICEAPI_H 1
+#define HAVE_AUDIOCLIENT_H 1
+#define HAVE_ENDPOINTVOLUME_H 1
 
 /* This is disabled by default to avoid C runtime dependencies and manifest requirements */
 #ifdef HAVE_LIBC
@@ -139,6 +142,8 @@
 #define HAVE__COPYSIGN  1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -188,9 +193,13 @@
 /* Enable various input drivers */
 #define SDL_JOYSTICK_DINPUT 1
 #define SDL_JOYSTICK_XINPUT 1
+#define SDL_JOYSTICK_HIDAPI 1
 #define SDL_HAPTIC_DINPUT   1
 #define SDL_HAPTIC_XINPUT   1
 
+/* Enable the dummy sensor driver */
+#define SDL_SENSOR_DUMMY  1
+
 /* Enable various shared object loading systems */
 #define SDL_LOADSO_WINDOWS  1
 
diff --git a/source/include/SDL_config_winrt.h b/source/include/SDL_config_winrt.h
index aac0e60..e3fe55b 100644
--- a/source/include/SDL_config_winrt.h
+++ b/source/include/SDL_config_winrt.h
@@ -97,6 +97,11 @@
 #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP
 #define HAVE_XINPUT_H 1
 #endif
+
+#define HAVE_MMDEVICEAPI_H 1
+#define HAVE_AUDIOCLIENT_H 1
+#define HAVE_ENDPOINTVOLUME_H 1
+
 #define HAVE_LIBC 1
 #define STDC_HEADERS 1
 #define HAVE_CTYPE_H 1
@@ -155,6 +160,8 @@
 #define HAVE__COPYSIGN 1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -190,6 +197,9 @@
 #define SDL_HAPTIC_XINPUT   1
 #endif
 
+/* Enable the dummy sensor driver */
+#define SDL_SENSOR_DUMMY  1
+
 /* Enable various shared object loading systems */
 #define SDL_LOADSO_WINDOWS  1
 
diff --git a/source/include/SDL_config_wiz.h b/source/include/SDL_config_wiz.h
index fe86d5e..b6c00d0 100644
--- a/source/include/SDL_config_wiz.h
+++ b/source/include/SDL_config_wiz.h
@@ -94,6 +94,8 @@
 #define HAVE_COPYSIGNF  1
 #define HAVE_COS    1
 #define HAVE_COSF   1
+#define HAVE_EXP    1
+#define HAVE_EXPF   1
 #define HAVE_FABS   1
 #define HAVE_FABSF  1
 #define HAVE_FLOOR  1
@@ -127,6 +129,8 @@
 #define SDL_JOYSTICK_LINUX 1
 #define SDL_HAPTIC_LINUX 1
 
+#define SDL_SENSOR_DUMMY  1
+
 #define SDL_LOADSO_DLOPEN 1
 
 #define SDL_THREAD_PTHREAD 1
diff --git a/source/include/SDL_cpuinfo.h b/source/include/SDL_cpuinfo.h
index 0812705..ee3a47e 100644
--- a/source/include/SDL_cpuinfo.h
+++ b/source/include/SDL_cpuinfo.h
@@ -51,16 +51,19 @@
 #include <intrin.h>
 #else
 #ifdef __ALTIVEC__
-#if HAVE_ALTIVEC_H && !defined(__APPLE_ALTIVEC__) && !defined(SDL_DISABLE_ALTIVEC_H)
+#if defined(HAVE_ALTIVEC_H) && !defined(__APPLE_ALTIVEC__) && !defined(SDL_DISABLE_ALTIVEC_H)
 #include <altivec.h>
 #undef pixel
 #undef bool
 #endif
 #endif
+#if defined(__ARM_NEON__) && !defined(SDL_DISABLE_ARM_NEON_H)
+#include <arm_neon.h>
+#endif
 #if defined(__3dNOW__) && !defined(SDL_DISABLE_MM3DNOW_H)
 #include <mm3dnow.h>
 #endif
-#if HAVE_IMMINTRIN_H && !defined(SDL_DISABLE_IMMINTRIN_H)
+#if defined(HAVE_IMMINTRIN_H) && !defined(SDL_DISABLE_IMMINTRIN_H)
 #include <immintrin.h>
 #else
 #if defined(__MMX__) && !defined(SDL_DISABLE_MMINTRIN_H)
@@ -160,6 +163,11 @@
 extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX2(void);
 
 /**
+ *  This function returns true if the CPU has AVX-512F (foundation) features.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasAVX512F(void);
+
+/**
  *  This function returns true if the CPU has NEON (ARM SIMD) features.
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_HasNEON(void);
@@ -168,7 +176,6 @@
  *  This function returns the amount of RAM configured in the system, in MB.
  */
 extern DECLSPEC int SDLCALL SDL_GetSystemRAM(void);
-
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
diff --git a/source/include/SDL_events.h b/source/include/SDL_events.h
index 3d39e6a..af22eb6 100644
--- a/source/include/SDL_events.h
+++ b/source/include/SDL_events.h
@@ -85,6 +85,9 @@
                                      Called on Android in onResume()
                                 */
 
+    /* Display events */
+    SDL_DISPLAYEVENT   = 0x150,  /**< Display state change */
+
     /* Window events */
     SDL_WINDOWEVENT    = 0x200, /**< Window state change */
     SDL_SYSWMEVENT,             /**< System specific event */
@@ -144,6 +147,9 @@
     SDL_AUDIODEVICEADDED = 0x1100, /**< A new audio device is available */
     SDL_AUDIODEVICEREMOVED,        /**< An audio device has been removed. */
 
+    /* Sensor events */
+    SDL_SENSORUPDATE = 0x1200,     /**< A sensor was updated */
+
     /* Render events */
     SDL_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
     SDL_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */
@@ -167,6 +173,21 @@
     Uint32 type;
     Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
 } SDL_CommonEvent;
+
+/**
+ *  \brief Display state change event data (event.display.*)
+ */
+typedef struct SDL_DisplayEvent
+{
+    Uint32 type;        /**< ::SDL_DISPLAYEVENT */
+    Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
+    Uint32 display;     /**< The associated display index */
+    Uint8 event;        /**< ::SDL_DisplayEventID */
+    Uint8 padding1;
+    Uint8 padding2;
+    Uint8 padding3;
+    Sint32 data1;       /**< event dependent data */
+} SDL_DisplayEvent;
 
 /**
  *  \brief Window state change event data (event.window.*)
@@ -472,6 +493,17 @@
 
 
 /**
+ *  \brief Sensor event structure (event.sensor.*)
+ */
+typedef struct SDL_SensorEvent
+{
+    Uint32 type;        /**< ::SDL_SENSORUPDATE */
+    Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
+    Sint32 which;       /**< The instance ID of the sensor */
+    float data[6];      /**< Up to 6 values from the sensor - additional values can be queried using SDL_SensorGetData() */
+} SDL_SensorEvent;
+
+/**
  *  \brief The "quit requested" event
  */
 typedef struct SDL_QuitEvent
@@ -526,6 +558,7 @@
 {
     Uint32 type;                    /**< Event type, shared with all events */
     SDL_CommonEvent common;         /**< Common event data */
+    SDL_DisplayEvent display;       /**< Window event data */
     SDL_WindowEvent window;         /**< Window event data */
     SDL_KeyboardEvent key;          /**< Keyboard event data */
     SDL_TextEditingEvent edit;      /**< Text editing event data */
@@ -542,6 +575,7 @@
     SDL_ControllerButtonEvent cbutton;  /**< Game Controller button event data */
     SDL_ControllerDeviceEvent cdevice;  /**< Game Controller device event data */
     SDL_AudioDeviceEvent adevice;   /**< Audio device event data */
+    SDL_SensorEvent sensor;         /**< Sensor event data */
     SDL_QuitEvent quit;             /**< Quit request event data */
     SDL_UserEvent user;             /**< Custom event data */
     SDL_SysWMEvent syswm;           /**< System dependent window event data */
diff --git a/source/include/SDL_gamecontroller.h b/source/include/SDL_gamecontroller.h
index 2e024be..6ae9c95 100644
--- a/source/include/SDL_gamecontroller.h
+++ b/source/include/SDL_gamecontroller.h
@@ -176,6 +176,14 @@
 extern DECLSPEC const char *SDLCALL SDL_GameControllerNameForIndex(int joystick_index);
 
 /**
+ *  Get the mapping of a game controller.
+ *  This can be called before any controllers are opened.
+ *
+ *  \return the mapping string.  Must be freed with SDL_free().  Returns NULL if no mapping is available
+ */
+extern DECLSPEC char *SDLCALL SDL_GameControllerMappingForDeviceIndex(int joystick_index);
+
+/**
  *  Open a game controller for use.
  *  The index passed as an argument refers to the N'th game controller on the system.
  *  This index is not the value which will identify this controller in future
@@ -195,6 +203,13 @@
  *  Return the name for this currently opened controller
  */
 extern DECLSPEC const char *SDLCALL SDL_GameControllerName(SDL_GameController *gamecontroller);
+
+/**
+ *  Get the player index of an opened game controller, or -1 if it's not available
+ *
+ *  For XInput controllers this returns the XInput user index.
+ */
+extern DECLSPEC int SDLCALL SDL_GameControllerGetPlayerIndex(SDL_GameController *gamecontroller);
 
 /**
  *  Get the USB vendor ID of an opened controller, if available.
@@ -346,6 +361,19 @@
                                                           SDL_GameControllerButton button);
 
 /**
+ *  Trigger a rumble effect
+ *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ *  \param gamecontroller The controller to vibrate
+ *  \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
+ *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
+ *  \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ *  \return 0, or -1 if rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
+/**
  *  Close a controller previously opened with SDL_GameControllerOpen().
  */
 extern DECLSPEC void SDLCALL SDL_GameControllerClose(SDL_GameController *gamecontroller);
diff --git a/source/include/SDL_haptic.h b/source/include/SDL_haptic.h
index e3a2bca..2ea1bfc 100644
--- a/source/include/SDL_haptic.h
+++ b/source/include/SDL_haptic.h
@@ -117,6 +117,17 @@
 extern "C" {
 #endif /* __cplusplus */
 
+/* FIXME: For SDL 2.1, adjust all the magnitude variables to be Uint16 (0xFFFF).
+ *
+ * At the moment the magnitude variables are mixed between signed/unsigned, and
+ * it is also not made clear that ALL of those variables expect a max of 0x7FFF.
+ *
+ * Some platforms may have higher precision than that (Linux FF, Windows XInput)
+ * so we should fix the inconsistency in favor of higher possible precision,
+ * adjusting for platforms that use different scales.
+ * -flibit
+ */
+
 /**
  *  \typedef SDL_Haptic
  *
@@ -656,8 +667,8 @@
  * This struct is exclusively for the ::SDL_HAPTIC_LEFTRIGHT effect.
  *
  * The Left/Right effect is used to explicitly control the large and small
- * motors, commonly found in modern game controllers. One motor is high
- * frequency, the other is low frequency.
+ * motors, commonly found in modern game controllers. The small (right) motor
+ * is high frequency, and the large (left) motor is low frequency.
  *
  * \sa SDL_HAPTIC_LEFTRIGHT
  * \sa SDL_HapticEffect
@@ -668,7 +679,7 @@
     Uint16 type;            /**< ::SDL_HAPTIC_LEFTRIGHT */
 
     /* Replay */
-    Uint32 length;          /**< Duration of the effect. */
+    Uint32 length;          /**< Duration of the effect in milliseconds. */
 
     /* Rumble */
     Uint16 large_magnitude; /**< Control of the large controller motor. */
diff --git a/source/include/SDL_hints.h b/source/include/SDL_hints.h
index 3834640..4ee72e9 100644
--- a/source/include/SDL_hints.h
+++ b/source/include/SDL_hints.h
@@ -263,6 +263,16 @@
 #define SDL_HINT_GRAB_KEYBOARD              "SDL_GRAB_KEYBOARD"
 
 /**
+ *  \brief  A variable setting the double click time, in milliseconds.
+ */
+#define SDL_HINT_MOUSE_DOUBLE_CLICK_TIME    "SDL_MOUSE_DOUBLE_CLICK_TIME"
+
+/**
+ *  \brief  A variable setting the double click radius, in pixels.
+ */
+#define SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS    "SDL_MOUSE_DOUBLE_CLICK_RADIUS"
+
+/**
  *  \brief  A variable setting the speed scale for mouse motion, in floating point, when the mouse is not in relative mode
  */
 #define SDL_HINT_MOUSE_NORMAL_SPEED_SCALE    "SDL_MOUSE_NORMAL_SPEED_SCALE"
@@ -329,7 +339,7 @@
 #define SDL_HINT_IDLE_TIMER_DISABLED "SDL_IOS_IDLE_TIMER_DISABLED"
 
 /**
- *  \brief  A variable controlling which orientations are allowed on iOS.
+ *  \brief  A variable controlling which orientations are allowed on iOS/Android.
  *
  *  In some circumstances it is necessary to be able to explicitly control
  *  which UI orientations are allowed.
@@ -466,6 +476,88 @@
 #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
 
 /**
+ *  \brief  A variable controlling whether the HIDAPI joystick drivers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI drivers are not used
+ *    "1"       - HIDAPI drivers are used (the default)
+ *
+ *  This variable is the default for all drivers, but can be overridden by the hints for specific drivers below.
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI "SDL_JOYSTICK_HIDAPI"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for PS4 controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_PS4 "SDL_JOYSTICK_HIDAPI_PS4"
+
+/**
+ *  \brief  A variable controlling whether extended input reports should be used for PS4 controllers when using the HIDAPI driver.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - extended reports are not enabled (the default)
+ *    "1"       - extended reports
+ *
+ *  Extended input reports allow rumble on Bluetooth PS4 controllers, but
+ *  break DirectInput handling for applications that don't use SDL.
+ *
+ *  Once extended reports are enabled, they can not be disabled without
+ *  power cycling the controller.
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for Steam Controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_STEAM "SDL_JOYSTICK_HIDAPI_STEAM"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for Nintendo Switch controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH "SDL_JOYSTICK_HIDAPI_SWITCH"
+
+/**
+ *  \brief  A variable controlling whether the HIDAPI driver for XBox controllers should be used.
+ *
+ *  This variable can be set to the following values:
+ *    "0"       - HIDAPI driver is not used
+ *    "1"       - HIDAPI driver is used
+ *
+ *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_XBOX   "SDL_JOYSTICK_HIDAPI_XBOX"
+
+/**
+ *  \brief  A variable that controls whether Steam Controllers should be exposed using the SDL joystick and game controller APIs
+ *
+ *  The variable can be set to the following values:
+ *    "0"       - Do not scan for Steam Controllers
+ *    "1"       - Scan for Steam Controllers (the default)
+ *
+ *  The default value is "1".  This hint must be set before initializing the joystick subsystem.
+ */
+#define SDL_HINT_ENABLE_STEAM_CONTROLLERS "SDL_ENABLE_STEAM_CONTROLLERS"
+
+
+/**
  *  \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
  *      This is a debugging aid for developers and not expected to be used by end users. The default is "1"
  *
@@ -527,6 +619,10 @@
 *  This is specially useful if you build SDL against a non glibc libc library (such as musl) which
 *  provides a relatively small default thread stack size (a few kilobytes versus the default 8MB glibc uses).
 *  Support for this hint is currently available only in the pthread, Windows, and PSP backend.
+*
+*  Instead of this hint, in 2.0.9 and later, you can use
+*  SDL_CreateThreadWithStackSize(). This hint only works with the classic
+*  SDL_CreateThread().
 */
 #define SDL_HINT_THREAD_STACK_SIZE              "SDL_THREAD_STACK_SIZE"
 
@@ -753,6 +849,23 @@
 #define SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH "SDL_ANDROID_SEPARATE_MOUSE_AND_TOUCH"
 
  /**
+ * \brief A variable to control whether we trap the Android back button to handle it manually.
+ *        This is necessary for the right mouse button to work on some Android devices, or
+ *        to be able to trap the back button for use in your code reliably.  If set to true,
+ *        the back button will show up as an SDL_KEYDOWN / SDL_KEYUP pair with a keycode of 
+ *        SDL_SCANCODE_AC_BACK.
+ *
+ * The variable can be set to the following values:
+ *   "0"       - Back button will be handled as usual for system. (default)
+ *   "1"       - Back button will be trapped, allowing you to handle the key press
+ *               manually.  (This will also let right mouse click work on systems 
+ *               where the right mouse button functions as back.)
+ *
+ * The value of this hint is used at runtime, so it can be changed at any time.
+ */
+#define SDL_HINT_ANDROID_TRAP_BACK_BUTTON "SDL_ANDROID_TRAP_BACK_BUTTON"
+
+ /**
  * \brief A variable to control whether the return key on the soft keyboard
  *        should hide the soft keyboard on Android and iOS.
  *
diff --git a/source/include/SDL_joystick.h b/source/include/SDL_joystick.h
index f67772d..6e05a9c 100644
--- a/source/include/SDL_joystick.h
+++ b/source/include/SDL_joystick.h
@@ -97,10 +97,10 @@
 typedef enum
 {
     SDL_JOYSTICK_POWER_UNKNOWN = -1,
-    SDL_JOYSTICK_POWER_EMPTY,
-    SDL_JOYSTICK_POWER_LOW,
-    SDL_JOYSTICK_POWER_MEDIUM,
-    SDL_JOYSTICK_POWER_FULL,
+    SDL_JOYSTICK_POWER_EMPTY,   /* <= 5% */
+    SDL_JOYSTICK_POWER_LOW,     /* <= 20% */
+    SDL_JOYSTICK_POWER_MEDIUM,  /* <= 70% */
+    SDL_JOYSTICK_POWER_FULL,    /* <= 100% */
     SDL_JOYSTICK_POWER_WIRED,
     SDL_JOYSTICK_POWER_MAX
 } SDL_JoystickPowerLevel;
@@ -131,6 +131,12 @@
  *  If no name can be found, this function returns NULL.
  */
 extern DECLSPEC const char *SDLCALL SDL_JoystickNameForIndex(int device_index);
+
+/**
+ *  Get the player index of a joystick, or -1 if it's not available
+ *  This can be called before any joysticks are opened.
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickGetDevicePlayerIndex(int device_index);
 
 /**
  *  Return the GUID for the joystick at this index
@@ -193,6 +199,13 @@
  *  If no name can be found, this function returns NULL.
  */
 extern DECLSPEC const char *SDLCALL SDL_JoystickName(SDL_Joystick * joystick);
+
+/**
+ *  Get the player index of an opened joystick, or -1 if it's not available
+ *
+ *  For XInput controllers this returns the XInput user index.
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickGetPlayerIndex(SDL_Joystick * joystick);
 
 /**
  *  Return the GUID for this opened joystick
@@ -362,6 +375,19 @@
                                                     int button);
 
 /**
+ *  Trigger a rumble effect
+ *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ *  \param joystick The joystick to vibrate
+ *  \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
+ *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
+ *  \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ *  \return 0, or -1 if rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
+/**
  *  Close a joystick previously opened with SDL_JoystickOpen().
  */
 extern DECLSPEC void SDLCALL SDL_JoystickClose(SDL_Joystick * joystick);
diff --git a/source/include/SDL_sensor.h b/source/include/SDL_sensor.h
new file mode 100644
index 0000000..ac163a8
--- /dev/null
+++ b/source/include/SDL_sensor.h
@@ -0,0 +1,251 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/**
+ *  \file SDL_sensor.h
+ *
+ *  Include file for SDL sensor event handling
+ *
+ */
+
+#ifndef _SDL_sensor_h
+#define _SDL_sensor_h
+
+#include "SDL_stdinc.h"
+#include "SDL_error.h"
+
+#include "begin_code.h"
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+extern "C" {
+/* *INDENT-ON* */
+#endif
+
+/**
+ *  \brief SDL_sensor.h
+ *
+ *  In order to use these functions, SDL_Init() must have been called
+ *  with the ::SDL_INIT_SENSOR flag.  This causes SDL to scan the system
+ *  for sensors, and load appropriate drivers.
+ */
+
+struct _SDL_Sensor;
+typedef struct _SDL_Sensor SDL_Sensor;
+
+/**
+ * This is a unique ID for a sensor for the time it is connected to the system,
+ * and is never reused for the lifetime of the application.
+ *
+ * The ID value starts at 0 and increments from there. The value -1 is an invalid ID.
+ */
+typedef Sint32 SDL_SensorID;
+
+/* The different sensors defined by SDL
+ *
+ * Additional sensors may be available, using platform dependent semantics.
+ *
+ * Hare are the additional Android sensors:
+ * https://developer.android.com/reference/android/hardware/SensorEvent.html#values
+ */
+typedef enum
+{
+    SDL_SENSOR_INVALID = -1,    /**< Returned for an invalid sensor */
+    SDL_SENSOR_UNKNOWN,         /**< Unknown sensor type */
+    SDL_SENSOR_ACCEL,           /**< Accelerometer */
+    SDL_SENSOR_GYRO             /**< Gyroscope */
+} SDL_SensorType;
+
+/**
+ * Accelerometer sensor
+ *
+ * The accelerometer returns the current acceleration in SI meters per
+ * second squared. This includes gravity, so a device at rest will have
+ * an acceleration of SDL_STANDARD_GRAVITY straight down.
+ *
+ * values[0]: Acceleration on the x axis
+ * values[1]: Acceleration on the y axis
+ * values[2]: Acceleration on the z axis
+ *
+ * For phones held in portrait mode, the axes are defined as follows:
+ * -X ... +X : left ... right
+ * -Y ... +Y : bottom ... top
+ * -Z ... +Z : farther ... closer
+ * 
+ * The axis data is not changed when the phone is rotated.
+ *
+ * \sa SDL_GetDisplayOrientation()
+ */
+#define SDL_STANDARD_GRAVITY    9.80665f
+
+/**
+ * Gyroscope sensor
+ *
+ * The gyroscope returns the current rate of rotation in radians per second.
+ * The rotation is positive in the counter-clockwise direction. That is,
+ * an observer looking from a positive location on one of the axes would
+ * see positive rotation on that axis when it appeared to be rotating
+ * counter-clockwise.
+ *
+ * values[0]: Angular speed around the x axis
+ * values[1]: Angular speed around the y axis
+ * values[2]: Angular speed around the z axis
+ *
+ * For phones held in portrait mode, the axes are defined as follows:
+ * -X ... +X : left ... right
+ * -Y ... +Y : bottom ... top
+ * -Z ... +Z : farther ... closer
+ * 
+ * The axis data is not changed when the phone is rotated.
+ *
+ * \sa SDL_GetDisplayOrientation()
+ */
+
+/* Function prototypes */
+
+/**
+ *  \brief Count the number of sensors attached to the system right now
+ */
+extern DECLSPEC int SDLCALL SDL_NumSensors(void);
+
+/**
+ *  \brief Get the implementation dependent name of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ * 
+ *  \return The sensor name, or NULL if device_index is out of range.
+ */
+extern DECLSPEC const char *SDLCALL SDL_SensorGetDeviceName(int device_index);
+
+/**
+ *  \brief Get the type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor type, or SDL_SENSOR_INVALID if device_index is out of range.
+ */
+extern DECLSPEC SDL_SensorType SDLCALL SDL_SensorGetDeviceType(int device_index);
+
+/**
+ *  \brief Get the platform dependent type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor platform dependent type, or -1 if device_index is out of range.
+ */
+extern DECLSPEC int SDLCALL SDL_SensorGetDeviceNonPortableType(int device_index);
+
+/**
+ *  \brief Get the instance ID of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor instance ID, or -1 if device_index is out of range.
+ */
+extern DECLSPEC SDL_SensorID SDLCALL SDL_SensorGetDeviceInstanceID(int device_index);
+
+/**
+ *  \brief Open a sensor for use.
+ *
+ *  The index passed as an argument refers to the N'th sensor on the system.
+ *
+ *  \return A sensor identifier, or NULL if an error occurred.
+ */
+extern DECLSPEC SDL_Sensor *SDLCALL SDL_SensorOpen(int device_index);
+
+/**
+ * Return the SDL_Sensor associated with an instance id.
+ */
+extern DECLSPEC SDL_Sensor *SDLCALL SDL_SensorFromInstanceID(SDL_SensorID instance_id);
+
+/**
+ *  \brief Get the implementation dependent name of a sensor.
+ *
+ *  \return The sensor name, or NULL if the sensor is NULL.
+ */
+extern DECLSPEC const char *SDLCALL SDL_SensorGetName(SDL_Sensor *sensor);
+
+/**
+ *  \brief Get the type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor type, or SDL_SENSOR_INVALID if the sensor is NULL.
+ */
+extern DECLSPEC SDL_SensorType SDLCALL SDL_SensorGetType(SDL_Sensor *sensor);
+
+/**
+ *  \brief Get the platform dependent type of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor platform dependent type, or -1 if the sensor is NULL.
+ */
+extern DECLSPEC int SDLCALL SDL_SensorGetNonPortableType(SDL_Sensor *sensor);
+
+/**
+ *  \brief Get the instance ID of a sensor.
+ *
+ *  This can be called before any sensors are opened.
+ *
+ *  \return The sensor instance ID, or -1 if the sensor is NULL.
+ */
+extern DECLSPEC SDL_SensorID SDLCALL SDL_SensorGetInstanceID(SDL_Sensor *sensor);
+
+/**
+ *  Get the current state of an opened sensor.
+ *
+ *  The number of values and interpretation of the data is sensor dependent.
+ *
+ *  \param sensor The sensor to query
+ *  \param data A pointer filled with the current sensor state
+ *  \param num_values The number of values to write to data
+ *
+ *  \return 0 or -1 if an error occurred.
+ */
+extern DECLSPEC int SDLCALL SDL_SensorGetData(SDL_Sensor * sensor, float *data, int num_values);
+
+/**
+ *  Close a sensor previously opened with SDL_SensorOpen()
+ */
+extern DECLSPEC void SDLCALL SDL_SensorClose(SDL_Sensor * sensor);
+
+/**
+ *  Update the current state of the open sensors.
+ *
+ *  This is called automatically by the event loop if sensor events are enabled.
+ *
+ *  This needs to be called from the thread that initialized the sensor subsystem.
+ */
+extern DECLSPEC void SDLCALL SDL_SensorUpdate(void);
+
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+/* *INDENT-OFF* */
+}
+/* *INDENT-ON* */
+#endif
+#include "close_code.h"
+
+#endif /* _SDL_sensor_h */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/include/SDL_stdinc.h b/source/include/SDL_stdinc.h
index 111a064..e373bc3 100644
--- a/source/include/SDL_stdinc.h
+++ b/source/include/SDL_stdinc.h
@@ -86,6 +86,28 @@
 #ifdef HAVE_FLOAT_H
 # include <float.h>
 #endif
+#if defined(HAVE_ALLOCA) && !defined(alloca)
+# if defined(HAVE_ALLOCA_H)
+#  include <alloca.h>
+# elif defined(__GNUC__)
+#  define alloca __builtin_alloca
+# elif defined(_MSC_VER)
+#  include <malloc.h>
+#  define alloca _alloca
+# elif defined(__WATCOMC__)
+#  include <malloc.h>
+# elif defined(__BORLANDC__)
+#  include <malloc.h>
+# elif defined(__DMC__)
+#  include <stdlib.h>
+# elif defined(__AIX__)
+#pragma alloca
+# elif defined(__MRC__)
+void *alloca(unsigned);
+# else
+char *alloca();
+# endif
+#endif
 
 /**
  *  The number of elements in an array.
@@ -328,28 +350,6 @@
 extern "C" {
 #endif
 
-#if defined(HAVE_ALLOCA) && !defined(alloca)
-# if defined(HAVE_ALLOCA_H)
-#  include <alloca.h>
-# elif defined(__GNUC__)
-#  define alloca __builtin_alloca
-# elif defined(_MSC_VER)
-#  include <malloc.h>
-#  define alloca _alloca
-# elif defined(__WATCOMC__)
-#  include <malloc.h>
-# elif defined(__BORLANDC__)
-#  include <malloc.h>
-# elif defined(__DMC__)
-#  include <stdlib.h>
-# elif defined(__AIX__)
-#pragma alloca
-# elif defined(__MRC__)
-void *alloca(unsigned);
-# else
-char *alloca();
-# endif
-#endif
 #ifdef HAVE_ALLOCA
 #define SDL_stack_alloc(type, count)    (type*)alloca(sizeof(type)*(count))
 #define SDL_stack_free(data)
@@ -445,12 +445,12 @@
 #endif
 }
 
-
 extern DECLSPEC void *SDLCALL SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
 
 extern DECLSPEC void *SDLCALL SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
 extern DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len);
 
+extern DECLSPEC wchar_t *SDLCALL SDL_wcsdup(const wchar_t *wstr);
 extern DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t *wstr);
 extern DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
 extern DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
@@ -514,6 +514,8 @@
 extern DECLSPEC float SDLCALL SDL_copysignf(float x, float y);
 extern DECLSPEC double SDLCALL SDL_cos(double x);
 extern DECLSPEC float SDLCALL SDL_cosf(float x);
+extern DECLSPEC double SDLCALL SDL_exp(double x);
+extern DECLSPEC float SDLCALL SDL_expf(float x);
 extern DECLSPEC double SDLCALL SDL_fabs(double x);
 extern DECLSPEC float SDLCALL SDL_fabsf(float x);
 extern DECLSPEC double SDLCALL SDL_floor(double x);
diff --git a/source/include/SDL_surface.h b/source/include/SDL_surface.h
index 45e5366..730d49f 100644
--- a/source/include/SDL_surface.h
+++ b/source/include/SDL_surface.h
@@ -249,6 +249,13 @@
                                             int flag, Uint32 key);
 
 /**
+ *  \brief Returns whether the surface has a color key
+ *
+ *  \return SDL_TRUE if the surface has a color key, or SDL_FALSE if the surface is NULL or has no color key
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasColorKey(SDL_Surface * surface);
+
+/**
  *  \brief Gets the color key (transparent pixel) in a blittable surface.
  *
  *  \param surface The surface to update
diff --git a/source/include/SDL_system.h b/source/include/SDL_system.h
index 7b776fd..4dc372d 100644
--- a/source/include/SDL_system.h
+++ b/source/include/SDL_system.h
@@ -76,6 +76,18 @@
 #endif /* __WIN32__ */
 
 
+/* Platform specific functions for Linux */
+#ifdef __LINUX__
+
+/**
+   \brief Sets the UNIX nice value for a thread, using setpriority() if possible, and RealtimeKit if available.
+
+   \return 0 on success, or -1 on error.
+ */
+extern DECLSPEC int SDLCALL SDL_LinuxSetThreadPriority(Sint64 threadID, int priority);
+ 
+#endif /* __LINUX__ */
+	
 /* Platform specific functions for iOS */
 #if defined(__IPHONEOS__) && __IPHONEOS__
 
@@ -112,6 +124,21 @@
    \brief Return true if the application is running on Android TV
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_IsAndroidTV(void);
+
+/**
+   \brief Return true if the application is running on a Chromebook
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsChromebook(void);
+
+/**
+  \brief Return true is the application is running on a Samsung DeX docking station
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsDeXMode(void);
+
+/**
+ \brief Trigger the Android system back button behavior.
+ */
+extern DECLSPEC void SDLCALL SDL_AndroidBackButton(void);
 
 /**
    See the official Android developer guide for more information:
@@ -236,6 +263,11 @@
 
 #endif /* __WINRT__ */
 
+/**
+ \brief Return true if the current device is a tablet.
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_IsTablet(void);
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }
diff --git a/source/include/SDL_syswm.h b/source/include/SDL_syswm.h
index 8aa4a39..f1c4021 100644
--- a/source/include/SDL_syswm.h
+++ b/source/include/SDL_syswm.h
@@ -33,12 +33,6 @@
 #include "SDL_video.h"
 #include "SDL_version.h"
 
-#include "begin_code.h"
-/* Set up for C function definitions, even when using C++ */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  *  \file SDL_syswm.h
  *
@@ -110,6 +104,12 @@
 #include "SDL_egl.h"
 #endif
 
+#include "begin_code.h"
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
  *  These are the various supported windowing subsystems
  */
diff --git a/source/include/SDL_thread.h b/source/include/SDL_thread.h
index 82a43fc..554dd0b 100644
--- a/source/include/SDL_thread.h
+++ b/source/include/SDL_thread.h
@@ -54,12 +54,13 @@
 /**
  *  The SDL thread priority.
  *
- *  \note On many systems you require special privileges to set high priority.
+ *  \note On many systems you require special privileges to set high or time critical priority.
  */
 typedef enum {
     SDL_THREAD_PRIORITY_LOW,
     SDL_THREAD_PRIORITY_NORMAL,
-    SDL_THREAD_PRIORITY_HIGH
+    SDL_THREAD_PRIORITY_HIGH,
+    SDL_THREAD_PRIORITY_TIME_CRITICAL
 } SDL_ThreadPriority;
 
 /**
@@ -105,14 +106,24 @@
                  pfnSDL_CurrentBeginThread pfnBeginThread,
                  pfnSDL_CurrentEndThread pfnEndThread);
 
+extern DECLSPEC SDL_Thread *SDLCALL
+SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
+                 const char *name, const size_t stacksize, void *data,
+                 pfnSDL_CurrentBeginThread pfnBeginThread,
+                 pfnSDL_CurrentEndThread pfnEndThread);
+
+
 /**
  *  Create a thread.
  */
 #if defined(SDL_CreateThread) && SDL_DYNAMIC_API
 #undef SDL_CreateThread
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
+#undef SDL_CreateThreadWithStackSize
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize_REAL(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
 #else
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthreadex, (pfnSDL_CurrentEndThread)_endthreadex)
 #endif
 
 #elif defined(__OS2__)
@@ -132,14 +143,30 @@
 SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data,
                  pfnSDL_CurrentBeginThread pfnBeginThread,
                  pfnSDL_CurrentEndThread pfnEndThread);
+extern DECLSPEC SDL_Thread *SDLCALL
+SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, const size_t stacksize, void *data,
+                 pfnSDL_CurrentBeginThread pfnBeginThread,
+                 pfnSDL_CurrentEndThread pfnEndThread);
 #if defined(SDL_CreateThread) && SDL_DYNAMIC_API
 #undef SDL_CreateThread
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
+#undef SDL_CreateThreadWithStackSize
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize_REAL(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
 #else
 #define SDL_CreateThread(fn, name, data) SDL_CreateThread(fn, name, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
+#define SDL_CreateThreadWithStackSize(fn, name, stacksize, data) SDL_CreateThreadWithStackSize(fn, name, stacksize, data, (pfnSDL_CurrentBeginThread)_beginthread, (pfnSDL_CurrentEndThread)_endthread)
 #endif
 
 #else
+
+/**
+ *  Create a thread with a default stack size.
+ *
+ *  This is equivalent to calling:
+ *  SDL_CreateThreadWithStackSize(fn, name, 0, data);
+ */
+extern DECLSPEC SDL_Thread *SDLCALL
+SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data);
 
 /**
  *  Create a thread.
@@ -158,9 +185,17 @@
  *   If a system imposes requirements, SDL will try to munge the string for
  *    it (truncate, etc), but the original string contents will be available
  *    from SDL_GetThreadName().
+ *
+ *   The size (in bytes) of the new stack can be specified. Zero means "use
+ *    the system default" which might be wildly different between platforms
+ *    (x86 Linux generally defaults to eight megabytes, an embedded device
+ *    might be a few kilobytes instead).
+ *
+ *   In SDL 2.1, stacksize will be folded into the original SDL_CreateThread
+ *    function.
  */
 extern DECLSPEC SDL_Thread *SDLCALL
-SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data);
+SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, const size_t stacksize, void *data);
 
 #endif
 
diff --git a/source/include/SDL_version.h b/source/include/SDL_version.h
index 584b48c..31443e1 100644
--- a/source/include/SDL_version.h
+++ b/source/include/SDL_version.h
@@ -59,7 +59,7 @@
 */
 #define SDL_MAJOR_VERSION   2
 #define SDL_MINOR_VERSION   0
-#define SDL_PATCHLEVEL      8
+#define SDL_PATCHLEVEL      9
 
 /**
  *  \brief Macro to determine SDL version program was compiled against.
diff --git a/source/include/SDL_video.h b/source/include/SDL_video.h
index 83f49fa..461f138 100644
--- a/source/include/SDL_video.h
+++ b/source/include/SDL_video.h
@@ -170,6 +170,24 @@
 } SDL_WindowEventID;
 
 /**
+ *  \brief Event subtype for display events
+ */
+typedef enum
+{
+    SDL_DISPLAYEVENT_NONE,          /**< Never used */
+    SDL_DISPLAYEVENT_ORIENTATION    /**< Display orientation has changed to data1 */
+} SDL_DisplayEventID;
+
+typedef enum
+{
+    SDL_ORIENTATION_UNKNOWN,            /**< The display orientation can't be determined */
+    SDL_ORIENTATION_LANDSCAPE,          /**< The display is in landscape mode, with the right side up, relative to portrait mode */
+    SDL_ORIENTATION_LANDSCAPE_FLIPPED,  /**< The display is in landscape mode, with the left side up, relative to portrait mode */
+    SDL_ORIENTATION_PORTRAIT,           /**< The display is in portrait mode */
+    SDL_ORIENTATION_PORTRAIT_FLIPPED    /**< The display is in portrait mode, upside down */
+} SDL_DisplayOrientation;
+
+/**
  *  \brief An opaque handle to an OpenGL context.
  */
 typedef void *SDL_GLContext;
@@ -317,18 +335,6 @@
 extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect);
 
 /**
- *  \brief Get the dots/pixels-per-inch for a display
- *
- *  \note Diagonal, horizontal and vertical DPI can all be optionally
- *        returned if the parameter is non-NULL.
- *
- *  \return 0 on success, or -1 if no DPI information is available or the index is out of range.
- *
- *  \sa SDL_GetNumVideoDisplays()
- */
-extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi);
-
-/**
  *  \brief Get the usable desktop area represented by a display, with the
  *         primary display located at 0,0
  *
@@ -348,6 +354,27 @@
 extern DECLSPEC int SDLCALL SDL_GetDisplayUsableBounds(int displayIndex, SDL_Rect * rect);
 
 /**
+ *  \brief Get the dots/pixels-per-inch for a display
+ *
+ *  \note Diagonal, horizontal and vertical DPI can all be optionally
+ *        returned if the parameter is non-NULL.
+ *
+ *  \return 0 on success, or -1 if no DPI information is available or the index is out of range.
+ *
+ *  \sa SDL_GetNumVideoDisplays()
+ */
+extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi);
+
+/**
+ *  \brief Get the orientation of a display
+ *
+ *  \return The orientation of the display, or SDL_ORIENTATION_UNKNOWN if it isn't available.
+ *
+ *  \sa SDL_GetNumVideoDisplays()
+ */
+extern DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetDisplayOrientation(int displayIndex);
+
+/**
  *  \brief Returns the number of available display modes.
  *
  *  \sa SDL_GetDisplayMode()
diff --git a/source/include/SDL_vulkan.h b/source/include/SDL_vulkan.h
index f04c21a..972cca4 100644
--- a/source/include/SDL_vulkan.h
+++ b/source/include/SDL_vulkan.h
@@ -135,11 +135,11 @@
  *  \brief Get the names of the Vulkan instance extensions needed to create
  *         a surface with \c SDL_Vulkan_CreateSurface().
  *
- *  \param [in]     window Window for which the required Vulkan instance
+ *  \param [in]     \c NULL or window Window for which the required Vulkan instance
  *                  extensions should be retrieved
- *  \param [in,out] count pointer to an \c unsigned related to the number of
+ *  \param [in,out] pCount pointer to an \c unsigned related to the number of
  *                  required Vulkan instance extensions
- *  \param [out]    names \c NULL or a pointer to an array to be filled with the
+ *  \param [out]    pNames \c NULL or a pointer to an array to be filled with the
  *                  required Vulkan instance extensions
  *
  *  \return \c SDL_TRUE on success, \c SDL_FALSE on error.
@@ -153,6 +153,10 @@
  *  is smaller than the number of required extensions, \c SDL_FALSE will be
  *  returned instead of \c SDL_TRUE, to indicate that not all the required
  *  extensions were returned.
+ * 
+ *  \note If \c window is not NULL, it will be checked against its creation
+ *        flags to ensure that the Vulkan flag is present. This parameter
+ *        will be removed in a future major release.
  *
  *  \note The returned list of extensions will contain \c VK_KHR_surface
  *        and zero or more platform specific extensions
@@ -160,12 +164,13 @@
  *  \note The extension names queried here must be enabled when calling
  *        VkCreateInstance, otherwise surface creation will fail.
  *
- *  \note \c window should have been created with the \c SDL_WINDOW_VULKAN flag.
+ *  \note \c window should have been created with the \c SDL_WINDOW_VULKAN flag
+ *        or be \c NULL
  *
  *  \code
  *  unsigned int count;
  *  // get count of required extensions
- *  if(!SDL_Vulkan_GetInstanceExtensions(window, &count, NULL))
+ *  if(!SDL_Vulkan_GetInstanceExtensions(NULL, &count, NULL))
  *      handle_error();
  *
  *  static const char *const additionalExtensions[] =
@@ -179,7 +184,7 @@
  *      handle_error();
  *
  *  // get names of required extensions
- *  if(!SDL_Vulkan_GetInstanceExtensions(window, &count, names))
+ *  if(!SDL_Vulkan_GetInstanceExtensions(NULL, &count, names))
  *      handle_error();
  *
  *  // copy additional extensions after required extensions
diff --git a/source/src/SDL.c b/source/src/SDL.c
index 0e55279..6d7e166 100644
--- a/source/src/SDL.c
+++ b/source/src/SDL.c
@@ -33,6 +33,7 @@
 #include "events/SDL_events_c.h"
 #include "haptic/SDL_haptic_c.h"
 #include "joystick/SDL_joystick_c.h"
+#include "sensor/SDL_sensor_c.h"
 
 /* Initialization/Cleanup routines */
 #if !SDL_TIMERS_DISABLED
@@ -123,11 +124,11 @@
     }
 
 #if SDL_VIDEO_DRIVER_WINDOWS
-	if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
-		if (SDL_HelperWindowCreate() < 0) {
-			return -1;
-		}
-	}
+    if ((flags & (SDL_INIT_HAPTIC|SDL_INIT_JOYSTICK))) {
+        if (SDL_HelperWindowCreate() < 0) {
+            return -1;
+        }
+    }
 #endif
 
 #if !SDL_TIMERS_DISABLED
@@ -232,6 +233,20 @@
 #endif
     }
 
+    /* Initialize the sensor subsystem */
+    if ((flags & SDL_INIT_SENSOR)){
+#if !SDL_SENSOR_DISABLED
+        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_SENSOR)) {
+            if (SDL_SensorInit() < 0) {
+                return (-1);
+            }
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_SENSOR);
+#else
+        return SDL_SetError("SDL not built with sensor support");
+#endif
+    }
+
     return (0);
 }
 
@@ -245,6 +260,15 @@
 SDL_QuitSubSystem(Uint32 flags)
 {
     /* Shut down requested initialized subsystems */
+#if !SDL_SENSOR_DISABLED
+    if ((flags & SDL_INIT_SENSOR)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_SENSOR)) {
+            SDL_SensorQuit();
+        }
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_SENSOR);
+    }
+#endif
+
 #if !SDL_JOYSTICK_DISABLED
     if ((flags & SDL_INIT_GAMECONTROLLER)) {
         /* game controller implies joystick */
@@ -451,6 +475,20 @@
 #endif
 }
 
+SDL_bool
+SDL_IsTablet()
+{
+#if __ANDROID__
+    extern SDL_bool SDL_IsAndroidTablet(void);
+    return SDL_IsAndroidTablet();
+#elif __IPHONEOS__
+    extern SDL_bool SDL_IsIPad(void);
+    return SDL_IsIPad();
+#else
+    return SDL_FALSE;
+#endif
+}
+
 #if defined(__WIN32__)
 
 #if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB)
diff --git a/source/src/SDL_assert.c b/source/src/SDL_assert.c
index 76f5d60..1ca083a 100644
--- a/source/src/SDL_assert.c
+++ b/source/src/SDL_assert.c
@@ -120,10 +120,17 @@
 }
 
 
-static SDL_NORETURN void SDL_ExitProcess(int exitcode)
+#if defined(__WATCOMC__)
+#pragma aux SDL_ExitProcess aborts;
+#endif
+static void SDL_ExitProcess(int exitcode)
 {
 #ifdef __WIN32__
-    ExitProcess(exitcode);
+    /* "if you do not know the state of all threads in your process, it is
+       better to call TerminateProcess than ExitProcess"
+       https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx */
+    TerminateProcess(GetCurrentProcess(), exitcode);
+
 #elif defined(__EMSCRIPTEN__)
     emscripten_cancel_main_loop();  /* this should "kill" the app. */
     emscripten_force_exit(exitcode);  /* this should "kill" the app. */
@@ -134,7 +141,10 @@
 }
 
 
-static SDL_NORETURN void SDL_AbortAssertion(void)
+#if defined(__WATCOMC__)
+#pragma aux SDL_AbortAssertion aborts;
+#endif
+static void SDL_AbortAssertion(void)
 {
     SDL_Quit();
     SDL_ExitProcess(42);
diff --git a/source/src/SDL_assert_c.h b/source/src/SDL_assert_c.h
index aa690a3..93263d6 100644
--- a/source/src/SDL_assert_c.h
+++ b/source/src/SDL_assert_c.h
@@ -19,6 +19,11 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_assert_c_h_
+#define SDL_assert_c_h_
+
 extern void SDL_AssertionsQuit(void);
 
+#endif /* SDL_assert_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/atomic/SDL_atomic.c b/source/src/atomic/SDL_atomic.c
index df49201..d0022cd 100644
--- a/source/src/atomic/SDL_atomic.c
+++ b/source/src/atomic/SDL_atomic.c
@@ -53,10 +53,11 @@
 #endif
 
 #if defined(__WATCOMC__) && defined(__386__)
+SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));
 #define HAVE_WATCOM_ATOMICS
 extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
 #pragma aux _SDL_xchg_watcom = \
-  "xchg [ecx], eax" \
+  "lock xchg [ecx], eax" \
   parm [ecx] [eax] \
   value [eax] \
   modify exact [eax];
diff --git a/source/src/atomic/SDL_spinlock.c b/source/src/atomic/SDL_spinlock.c
index 1ebc718..6a7b14a 100644
--- a/source/src/atomic/SDL_spinlock.c
+++ b/source/src/atomic/SDL_spinlock.c
@@ -32,11 +32,15 @@
 #include <atomic.h>
 #endif
 
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include <xmmintrin.h>
+#endif
+
 #if defined(__WATCOMC__) && defined(__386__)
 SDL_COMPILE_TIME_ASSERT(locksize, 4==sizeof(SDL_SpinLock));
 extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
 #pragma aux _SDL_xchg_watcom = \
-  "xchg [ecx], eax" \
+  "lock xchg [ecx], eax" \
   parm [ecx] [eax] \
   value [eax] \
   modify exact [eax];
@@ -116,12 +120,32 @@
 #endif
 }
 
+/* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
+#if (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
+    #define PAUSE_INSTRUCTION() __asm__ __volatile__("pause\n")  /* Some assemblers can't do REP NOP, so go with PAUSE. */
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+    #define PAUSE_INSTRUCTION() _mm_pause()  /* this is actually "rep nop" and not a SIMD instruction. No inline asm in MSVC x86-64! */
+#elif defined(__WATCOMC__) && defined(__386__)
+    /* watcom assembler rejects PAUSE if CPU < i686, and it refuses REP NOP as an invalid combination. Hardcode the bytes.  */
+    extern _inline void PAUSE_INSTRUCTION(void);
+    #pragma aux PAUSE_INSTRUCTION = "db 0f3h,90h"
+#else
+    #define PAUSE_INSTRUCTION()
+#endif
+
 void
 SDL_AtomicLock(SDL_SpinLock *lock)
 {
+    int iterations = 0;
     /* FIXME: Should we have an eventual timeout? */
     while (!SDL_AtomicTryLock(lock)) {
-        SDL_Delay(0);
+        if (iterations < 32) {
+            iterations++;
+            PAUSE_INSTRUCTION();
+        } else {
+            /* !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms. */
+            SDL_Delay(0);
+        }
     }
 }
 
diff --git a/source/src/audio/SDL_audio.c b/source/src/audio/SDL_audio.c
index dcaebea..f4999f1 100644
--- a/source/src/audio/SDL_audio.c
+++ b/source/src/audio/SDL_audio.c
@@ -378,21 +378,57 @@
 add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
 {
     int retval = -1;
-    const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1;
-    SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size);
-    if (item == NULL) {
-        return -1;
-    }
+    SDL_AudioDeviceItem *item;
+    const SDL_AudioDeviceItem *i;
+    int dupenum = 0;
 
     SDL_assert(handle != NULL);  /* we reserve NULL, audio backends can't use it. */
+    SDL_assert(name != NULL);
 
+    item = (SDL_AudioDeviceItem *) SDL_malloc(sizeof (SDL_AudioDeviceItem));
+    if (!item) {
+        return SDL_OutOfMemory();
+    }
+
+    item->original_name = SDL_strdup(name);
+    if (!item->original_name) {
+        SDL_free(item);
+        return SDL_OutOfMemory();
+    }
+
+    item->dupenum = 0;
+    item->name = item->original_name;
     item->handle = handle;
-    SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem));
 
     SDL_LockMutex(current_audio.detectionLock);
+
+    for (i = *devices; i != NULL; i = i->next) {
+        if (SDL_strcmp(name, i->original_name) == 0) {
+            dupenum = i->dupenum + 1;
+            break;  /* stop at the highest-numbered dupe. */
+        }
+    }
+
+    if (dupenum) {
+        const size_t len = SDL_strlen(name) + 16;
+        char *replacement = (char *) SDL_malloc(len);
+        if (!replacement) {
+            SDL_UnlockMutex(current_audio.detectionLock);
+            SDL_free(item->original_name);
+            SDL_free(item);
+            SDL_OutOfMemory();
+            return -1;
+        }
+
+        SDL_snprintf(replacement, len, "%s (%d)", name, dupenum + 1);
+        item->dupenum = dupenum;
+        item->name = replacement;
+    }
+
     item->next = *devices;
     *devices = item;
-    retval = (*devCount)++;
+    retval = (*devCount)++;   /* !!! FIXME: this should be an atomic increment */
+
     SDL_UnlockMutex(current_audio.detectionLock);
 
     return retval;
@@ -420,6 +456,11 @@
         if (item->handle != NULL) {
             current_audio.impl.FreeDeviceHandle(item->handle);
         }
+        /* these two pointers are the same if not a duplicate devname */
+        if (item->name != item->original_name) {
+            SDL_free(item->name);
+        }
+        SDL_free(item->original_name);
         SDL_free(item);
     }
     *devices = NULL;
@@ -451,7 +492,11 @@
     SDL_assert(get_audio_device(device->id) == device);
 
     if (!SDL_AtomicGet(&device->enabled)) {
-        return;
+        return;  /* don't report disconnects more than once. */
+    }
+
+    if (SDL_AtomicGet(&device->shutdown)) {
+        return;  /* don't report disconnect if we're trying to close device. */
     }
 
     /* Ends the audio callback and mark the device as STOPPED, but the
@@ -651,7 +696,7 @@
     SDL_assert(!device->iscapture);
 
     /* The audio mixing is always a high priority thread */
-    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
 
     /* Perform any thread setup */
     device->threadid = SDL_ThreadID();
@@ -832,6 +877,8 @@
         }
     }
 
+    current_audio.impl.PrepareToClose(device);
+
     current_audio.impl.FlushCapture(device);
 
     current_audio.impl.ThreadDeinit(device);
@@ -971,6 +1018,11 @@
             } else {
                 *devices = next;
             }
+            /* these two pointers are the same if not a duplicate devname */
+            if (item->name != item->original_name) {
+                SDL_free(item->name);
+            }
+            SDL_free(item->original_name);
             SDL_free(item);
         }
         item = next;
@@ -997,7 +1049,6 @@
 
     if (!iscapture && current_audio.outputDevicesRemoved) {
         clean_out_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount, &current_audio.outputDevicesRemoved);
-        current_audio.outputDevicesRemoved = SDL_FALSE;
     }
 
     retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
@@ -1054,16 +1105,14 @@
         return;
     }
 
-    if (device->id > 0) {
-        SDL_AudioDevice *opendev = open_devices[device->id - 1];
-        SDL_assert((opendev == device) || (opendev == NULL));
-        if (opendev == device) {
-            open_devices[device->id - 1] = NULL;
-        }
-    }
-
+    /* make sure the device is paused before we do anything else, so the
+       audio callback definitely won't fire again. */
+    current_audio.impl.LockDevice(device);
+    SDL_AtomicSet(&device->paused, 1);
     SDL_AtomicSet(&device->shutdown, 1);
     SDL_AtomicSet(&device->enabled, 0);
+    current_audio.impl.UnlockDevice(device);
+
     if (device->thread != NULL) {
         SDL_WaitThread(device->thread, NULL);
     }
@@ -1073,6 +1122,14 @@
 
     SDL_free(device->work_buffer);
     SDL_FreeAudioStream(device->stream);
+
+    if (device->id > 0) {
+        SDL_AudioDevice *opendev = open_devices[device->id - 1];
+        SDL_assert((opendev == device) || (opendev == NULL));
+        if (opendev == device) {
+            open_devices[device->id - 1] = NULL;
+        }
+    }
 
     if (device->hidden != NULL) {
         current_audio.impl.CloseDevice(device);
@@ -1118,8 +1175,9 @@
         }
     case 1:                    /* Mono */
     case 2:                    /* Stereo */
-    case 4:                    /* surround */
-    case 6:                    /* surround with center and lfe */
+    case 4:                    /* Quadrophonic */
+    case 6:                    /* 5.1 surround */
+    case 8:                    /* 7.1 surround */
         break;
     default:
         SDL_SetError("Unsupported number of audio channels.");
@@ -1312,15 +1370,12 @@
             build_stream = SDL_TRUE;
         }
     }
-
-    /* !!! FIXME in 2.1: add SDL_AUDIO_ALLOW_SAMPLES_CHANGE flag?
-       As of 2.0.6, we will build a stream to buffer the difference between
-       what the app wants to feed and the device wants to eat, so everyone
-       gets their way. In prior releases, SDL would force the callback to
-       feed at the rate the device requested, adjusted for resampling.
-     */
     if (device->spec.samples != obtained->samples) {
-        build_stream = SDL_TRUE;
+        if (allowed_changes & SDL_AUDIO_ALLOW_SAMPLES_CHANGE) {
+            obtained->samples = device->spec.samples;
+        } else {
+            build_stream = SDL_TRUE;
+        }
     }
 
     SDL_CalculateAudioSpec(obtained);  /* recalc after possible changes. */
diff --git a/source/src/audio/SDL_audiocvt.c b/source/src/audio/SDL_audiocvt.c
index 7fde2b9..ee0ba32 100644
--- a/source/src/audio/SDL_audiocvt.c
+++ b/source/src/audio/SDL_audiocvt.c
@@ -724,7 +724,7 @@
     SDL_assert(format == AUDIO_F32SYS);
 
     /* we keep no streaming state here, so pad with silence on both ends. */
-    padding = (float *) SDL_calloc(paddingsamples, sizeof (float));
+    padding = (float *) SDL_calloc(paddingsamples ? paddingsamples : 1, sizeof (float));
     if (!padding) {
         SDL_OutOfMemory();
         return;
@@ -1291,7 +1291,7 @@
     retval->packetlen = packetlen;
     retval->rate_incr = ((double) dst_rate) / ((double) src_rate);
     retval->resampler_padding_samples = ResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels;
-    retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples, sizeof (float));
+    retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples ? retval->resampler_padding_samples : 1, sizeof (float));
 
     if (retval->resampler_padding == NULL) {
         SDL_FreeAudioStream(retval);
diff --git a/source/src/audio/SDL_audiodev_c.h b/source/src/audio/SDL_audiodev_c.h
index 15928d1..2d3b0ea 100644
--- a/source/src/audio/SDL_audiodev_c.h
+++ b/source/src/audio/SDL_audiodev_c.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_audiodev_c_h_
+#define SDL_audiodev_c_h_
+
 #include "SDL.h"
 #include "../SDL_internal.h"
 #include "SDL_sysaudio.h"
@@ -35,4 +39,6 @@
 
 extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int));
 
+#endif /* SDL_audiodev_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/audio/SDL_audiotypecvt.c b/source/src/audio/SDL_audiotypecvt.c
index 2fbd916..5f8cc22 100644
--- a/source/src/audio/SDL_audiotypecvt.c
+++ b/source/src/audio/SDL_audiotypecvt.c
@@ -25,8 +25,10 @@
 #include "SDL_cpuinfo.h"
 #include "SDL_assert.h"
 
-/* !!! FIXME: write NEON code. */
-#define HAVE_NEON_INTRINSICS 0
+/* !!! FIXME: disabled until we fix https://bugzilla.libsdl.org/show_bug.cgi?id=4186 */
+#if 0 /*def __ARM_NEON__*/
+#define HAVE_NEON_INTRINSICS 1
+#endif
 
 #ifdef __SSE2__
 #define HAVE_SSE2_INTRINSICS 1
@@ -62,7 +64,7 @@
 
 #define DIVBY128 0.0078125f
 #define DIVBY32768 0.000030517578125f
-#define DIVBY2147483648 0.00000000046566128730773926
+#define DIVBY8388607 0.00000011920930376163766f
 
 
 #if NEED_SCALAR_CONVERTER_FALLBACKS
@@ -152,7 +154,7 @@
     LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32");
 
     for (i = cvt->len_cvt / sizeof (Sint32); i; --i, ++src, ++dst) {
-        *dst = (float) (((double) *src) * DIVBY2147483648);
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
     }
 
     if (cvt->filters[++cvt->filter_index]) {
@@ -171,10 +173,10 @@
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 127;
-        } else if (sample < -1.0f) {
-            *dst = -127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
         } else {
             *dst = (Sint8)(sample * 127.0f);
         }
@@ -197,9 +199,9 @@
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 255;
-        } else if (sample < -1.0f) {
+        } else if (sample <= -1.0f) {
             *dst = 0;
         } else {
             *dst = (Uint8)((sample + 1.0f) * 127.0f);
@@ -223,10 +225,10 @@
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 32767;
-        } else if (sample < -1.0f) {
-            *dst = -32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
         } else {
             *dst = (Sint16)(sample * 32767.0f);
         }
@@ -249,9 +251,9 @@
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
-            *dst = 65534;
-        } else if (sample < -1.0f) {
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
             *dst = 0;
         } else {
             *dst = (Uint16)((sample + 1.0f) * 32767.0f);
@@ -275,12 +277,12 @@
 
     for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) {
         const float sample = *src;
-        if (sample > 1.0f) {
+        if (sample >= 1.0f) {
             *dst = 2147483647;
-        } else if (sample < -1.0f) {
-            *dst = -2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (Sint32) -2147483648LL;
         } else {
-            *dst = (Sint32)((double)sample * 2147483647.0);
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
         }
     }
 
@@ -509,16 +511,6 @@
     }
 }
 
-#if defined(__GNUC__) && (__GNUC__ < 4)
-/* these were added as of gcc-4.0: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19418 */
-static inline __m128 _mm_castsi128_ps(__m128i __A) {
-  return (__m128) __A;
-}
-static inline __m128i _mm_castps_si128(__m128 __A) {
-  return (__m128i) __A;
-}
-#endif
-
 static void SDLCALL
 SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
 {
@@ -530,7 +522,7 @@
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (Sint32); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (float) (((double) *src) * DIVBY2147483648);
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -538,15 +530,11 @@
 
     {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
-        const __m128d divby2147483648 = _mm_set1_pd(DIVBY2147483648);
+        const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607);
         const __m128i *mmsrc = (const __m128i *) src;
         while (i >= 4) {   /* 4 * sint32 */
-            const __m128i ints = _mm_load_si128(mmsrc);
-            /* bitshift the whole register over, so _mm_cvtepi32_pd can read the top ints in the bottom of the vector. */
-            const __m128d doubles1 = _mm_mul_pd(_mm_cvtepi32_pd(_mm_srli_si128(ints, 8)), divby2147483648);
-            const __m128d doubles2 = _mm_mul_pd(_mm_cvtepi32_pd(ints), divby2147483648);
-            /* convert to float32, bitshift/or to get these into a vector to store. */
-            _mm_store_ps(dst, _mm_castsi128_ps(_mm_or_si128(_mm_slli_si128(_mm_castps_si128(_mm_cvtpd_ps(doubles1)), 8), _mm_castps_si128(_mm_cvtpd_ps(doubles2)))));
+            /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
+            _mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607));
             i -= 4; mmsrc++; dst += 4;
         }
         src = (const Sint32 *) mmsrc;
@@ -554,7 +542,7 @@
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (float) (((double) *src) * DIVBY2147483648);
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
         i--; src++; dst++;
     }
 
@@ -574,7 +562,14 @@
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Sint8) (*src * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -582,13 +577,15 @@
     /* Make sure src is aligned too. */
     if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         const __m128 mulby127 = _mm_set1_ps(127.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 16) {   /* 16 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+8), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+12), mulby127));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+8)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+12)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_packs_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4)));  /* pack down, store out. */
             i -= 16; src += 16; mmdst++;
         }
@@ -597,7 +594,14 @@
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Sint8) (*src * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -618,7 +622,14 @@
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Uint8) ((*src + 1.0f) * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -626,14 +637,15 @@
     /* Make sure src is aligned too. */
     if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
-        const __m128 add1 = _mm_set1_ps(1.0f);
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         const __m128 mulby127 = _mm_set1_ps(127.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 16) {   /* 16 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src), add1), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+4), add1), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+8), add1), mulby127));  /* load 4 floats, convert to sint32 */
-            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+12), add1), mulby127));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+8)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+12)), one), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_packus_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4)));  /* pack down, store out. */
             i -= 16; src += 16; mmdst++;
         }
@@ -642,7 +654,14 @@
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Uint8) ((*src + 1.0f) * 127.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -663,7 +682,14 @@
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Sint16) (*src * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -671,11 +697,13 @@
     /* Make sure src is aligned too. */
     if ((((size_t) src) & 15) == 0) {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         const __m128 mulby32767 = _mm_set1_ps(32767.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 8) {   /* 8 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby32767));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby32767));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_packs_epi32(ints1, ints2));  /* pack to sint16, store out. */
             i -= 8; src += 8; mmdst++;
         }
@@ -684,7 +712,14 @@
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Sint16) (*src * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -705,7 +740,14 @@
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Uint16) ((*src + 1.0f) * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -722,10 +764,12 @@
            though it looks like dark magic. */
         const __m128 mulby32767 = _mm_set1_ps(32767.0f);
         const __m128i topbit = _mm_set1_epi16(-32768);
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 8) {   /* 8 * float32 */
-            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby32767));  /* load 4 floats, convert to sint32 */
-            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby32767));  /* load 4 floats, convert to sint32 */
+            const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src+4)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
             _mm_store_si128(mmdst, _mm_xor_si128(_mm_packs_epi32(ints1, ints2), topbit));  /* pack to sint16, xor top bit, store out. */
             i -= 8; src += 8; mmdst++;
         }
@@ -734,7 +778,14 @@
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Uint16) ((*src + 1.0f) * 32767.0f);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
         i--; src++; dst++;
     }
 
@@ -755,7 +806,14 @@
 
     /* Get dst aligned to 16 bytes */
     for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
-        *dst = (Sint32) (((double) *src) * 2147483647.0);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (Sint32) -2147483648LL;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
     }
 
     SDL_assert(!i || ((((size_t) dst) & 15) == 0));
@@ -763,14 +821,12 @@
 
     {
         /* Aligned! Do SSE blocks as long as we have 16 bytes available. */
-        const __m128d mulby2147483647 = _mm_set1_pd(2147483647.0);
+        const __m128 one = _mm_set1_ps(1.0f);
+        const __m128 negone = _mm_set1_ps(-1.0f);
+        const __m128 mulby8388607 = _mm_set1_ps(8388607.0f);
         __m128i *mmdst = (__m128i *) dst;
         while (i >= 4) {   /* 4 * float32 */
-            const __m128 floats = _mm_load_ps(src);
-            /* bitshift the whole register over, so _mm_cvtps_pd can read the top floats in the bottom of the vector. */
-            const __m128d doubles1 = _mm_mul_pd(_mm_cvtps_pd(_mm_castsi128_ps(_mm_srli_si128(_mm_castps_si128(floats), 8))), mulby2147483647);
-            const __m128d doubles2 = _mm_mul_pd(_mm_cvtps_pd(floats), mulby2147483647);
-            _mm_store_si128(mmdst, _mm_or_si128(_mm_slli_si128(_mm_cvtpd_epi32(doubles1), 8), _mm_cvtpd_epi32(doubles2)));
+            _mm_store_si128(mmdst, _mm_slli_epi32(_mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby8388607)), 8));  /* load 4 floats, clamp, convert to sint32 */
             i -= 4; src += 4; mmdst++;
         }
         dst = (Sint32 *) mmdst;
@@ -778,7 +834,14 @@
 
     /* Finish off any leftovers with scalar operations. */
     while (i) {
-        *dst = (Sint32) (((double) *src) * 2147483647.0);
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = (Sint32) -2147483648LL;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
         i--; src++; dst++;
     }
 
@@ -787,6 +850,538 @@
     }
 }
 #endif
+
+
+#if HAVE_NEON_INTRINSICS
+static void SDLCALL
+SDL_Convert_S8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Sint8 *src = ((const Sint8 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 4)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_S8", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) {
+        *dst = ((float) *src) * DIVBY128;
+    }
+
+    src -= 15; dst -= 15;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const int8_t *mmsrc = (const int8_t *) src;
+        const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
+        while (i >= 16) {   /* 16 * 8-bit */
+            const int8x16_t bytes = vld1q_s8(mmsrc);  /* get 16 sint8 into a NEON register. */
+            const int16x8_t int16hi = vmovl_s8(vget_high_s8(bytes));  /* convert top 8 bytes to 8 int16 */
+            const int16x8_t int16lo = vmovl_s8(vget_low_s8(bytes));   /* convert bottom 8 bytes to 8 int16 */
+            /* split int16 to two int32, then convert to float, then multiply to normalize, store. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16hi))), divby128));
+            vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16hi))), divby128));
+            vst1q_f32(dst+8, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16lo))), divby128));
+            vst1q_f32(dst+12, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16lo))), divby128));
+            i -= 16; mmsrc -= 16; dst -= 16;
+        }
+
+        src = (const Sint8 *) mmsrc;
+    }
+
+    src += 15; dst += 15;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) *src) * DIVBY128;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_U8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Uint8 *src = ((const Uint8 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 4)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_U8", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+    }
+
+    src -= 15; dst -= 15;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const uint8_t *mmsrc = (const uint8_t *) src;
+        const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        while (i >= 16) {   /* 16 * 8-bit */
+            const uint8x16_t bytes = vld1q_u8(mmsrc);  /* get 16 uint8 into a NEON register. */
+            const uint16x8_t uint16hi = vmovl_u8(vget_high_u8(bytes));  /* convert top 8 bytes to 8 uint16 */
+            const uint16x8_t uint16lo = vmovl_u8(vget_low_u8(bytes));   /* convert bottom 8 bytes to 8 uint16 */
+            /* split uint16 to two uint32, then convert to float, then multiply to normalize, subtract to adjust for sign, store. */
+            vst1q_f32(dst, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16hi))), divby128, one));
+            vst1q_f32(dst+4, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16hi))), divby128, one));
+            vst1q_f32(dst+8, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16lo))), divby128, one));
+            vst1q_f32(dst+12, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16lo))), divby128, one));
+            i -= 16; mmsrc -= 16; dst -= 16;
+        }
+
+        src = (const Uint8 *) mmsrc;
+    }
+
+    src += 15; dst += 15;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = (((float) *src) * DIVBY128) - 1.0f;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_S16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Sint16 *src = ((const Sint16 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 2)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_S16", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt / sizeof (Sint16); i && (((size_t) (dst-7)) & 15); --i, --src, --dst) {
+        *dst = ((float) *src) * DIVBY32768;
+    }
+
+    src -= 7; dst -= 7;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768);
+        while (i >= 8) {   /* 8 * 16-bit */
+            const int16x8_t ints = vld1q_s16((int16_t const *) src);  /* get 8 sint16 into a NEON register. */
+            /* split int16 to two int32, then convert to float, then multiply to normalize, store. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(ints))), divby32768));
+            vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(ints))), divby32768));
+            i -= 8; src -= 8; dst -= 8;
+        }
+    }
+
+    src += 7; dst += 7;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) *src) * DIVBY32768;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_U16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Uint16 *src = ((const Uint16 *) (cvt->buf + cvt->len_cvt)) - 1;
+    float *dst = ((float *) (cvt->buf + cvt->len_cvt * 2)) - 1;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_U16", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
+    for (i = cvt->len_cvt / sizeof (Sint16); i && (((size_t) (dst-7)) & 15); --i, --src, --dst) {
+        *dst = (((float) *src) * DIVBY32768) - 1.0f;
+    }
+
+    src -= 7; dst -= 7;  /* adjust to read NEON blocks from the start. */
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768);
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        while (i >= 8) {   /* 8 * 16-bit */
+            const uint16x8_t uints = vld1q_u16((uint16_t const *) src);  /* get 8 uint16 into a NEON register. */
+            /* split uint16 to two int32, then convert to float, then multiply to normalize, subtract for sign, store. */
+            vst1q_f32(dst, vmlsq_f32(one, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uints))), divby32768));
+            vst1q_f32(dst+4, vmlsq_f32(one, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uints))), divby32768));
+            i -= 8; src -= 8; dst -= 8;
+        }
+    }
+
+    src += 7; dst += 7;  /* adjust for any scalar finishing. */
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = (((float) *src) * DIVBY32768) - 1.0f;
+        i--; src--; dst--;
+    }
+
+    cvt->len_cvt *= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_S32_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const Sint32 *src = (const Sint32 *) cvt->buf;
+    float *dst = (float *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (Sint32); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+    SDL_assert(!i || ((((size_t) src) & 15) == 0));
+
+    {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t divby8388607 = vdupq_n_f32(DIVBY8388607);
+        const int32_t *mmsrc = (const int32_t *) src;
+        while (i >= 4) {   /* 4 * sint32 */
+            /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
+            vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vshrq_n_s32(vld1q_s32(mmsrc), 8)), divby8388607));
+            i -= 4; mmsrc += 4; dst += 4;
+        }
+        src = (const Sint32 *) mmsrc;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        *dst = ((float) (*src>>8)) * DIVBY8388607;
+        i--; src++; dst++;
+    }
+
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_S8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Sint8 *dst = (Sint8 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S8 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby127 = vdupq_n_f32(127.0f);
+        int8_t *mmdst = (int8_t *) dst;
+        while (i >= 16) {   /* 16 * float32 */
+            const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints3 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+8)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints4 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+12)), one), mulby127));  /* load 4 floats, clamp, convert to sint32 */
+            const int8x8_t i8lo = vmovn_s16(vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2))); /* narrow to sint16, combine, narrow to sint8 */
+            const int8x8_t i8hi = vmovn_s16(vcombine_s16(vmovn_s32(ints3), vmovn_s32(ints4))); /* narrow to sint16, combine, narrow to sint8 */
+            vst1q_s8(mmdst, vcombine_s8(i8lo, i8hi));  /* combine to int8x16_t, store out */
+            i -= 16; src += 16; mmdst += 16;
+        }
+        dst = (Sint8 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 127;
+        } else if (sample <= -1.0f) {
+            *dst = -128;
+        } else {
+            *dst = (Sint8)(sample * 127.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_S8);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_U8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Uint8 *dst = (Uint8 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U8 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby127 = vdupq_n_f32(127.0f);
+        uint8_t *mmdst = (uint8_t *) dst;
+        while (i >= 16) {   /* 16 * float32 */
+            const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints3 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+8)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints4 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+12)), one), one), mulby127));  /* load 4 floats, clamp, convert to uint32 */
+            const uint8x8_t ui8lo = vmovn_u16(vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2))); /* narrow to uint16, combine, narrow to uint8 */
+            const uint8x8_t ui8hi = vmovn_u16(vcombine_u16(vmovn_u32(uints3), vmovn_u32(uints4))); /* narrow to uint16, combine, narrow to uint8 */
+            vst1q_u8(mmdst, vcombine_u8(ui8lo, ui8hi));  /* combine to uint8x16_t, store out */
+            i -= 16; src += 16; mmdst += 16;
+        }
+
+        dst = (Uint8 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 255;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint8)((sample + 1.0f) * 127.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 4;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_U8);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_S16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Sint16 *dst = (Sint16 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S16 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby32767 = vdupq_n_f32(32767.0f);
+        int16_t *mmdst = (int16_t *) dst;
+        while (i >= 8) {   /* 8 * float32 */
+            const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), mulby32767));  /* load 4 floats, clamp, convert to sint32 */
+            vst1q_s16(mmdst, vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2)));  /* narrow to sint16, combine, store out. */
+            i -= 8; src += 8; mmdst += 8;
+        }
+        dst = (Sint16 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 32767;
+        } else if (sample <= -1.0f) {
+            *dst = -32768;
+        } else {
+            *dst = (Sint16)(sample * 32767.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_S16SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_U16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Uint16 *dst = (Uint16 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U16 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+
+    /* Make sure src is aligned too. */
+    if ((((size_t) src) & 15) == 0) {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby32767 = vdupq_n_f32(32767.0f);
+        uint16_t *mmdst = (uint16_t *) dst;
+        while (i >= 8) {   /* 8 * float32 */
+            const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby32767));  /* load 4 floats, clamp, convert to uint32 */
+            const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), one), mulby32767));  /* load 4 floats, clamp, convert to uint32 */
+            vst1q_u16(mmdst, vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2)));  /* narrow to uint16, combine, store out. */
+            i -= 8; src += 8; mmdst += 8;
+        }
+        dst = (Uint16 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 65535;
+        } else if (sample <= -1.0f) {
+            *dst = 0;
+        } else {
+            *dst = (Uint16)((sample + 1.0f) * 32767.0f);
+        }
+        i--; src++; dst++;
+    }
+
+    cvt->len_cvt /= 2;
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_U16SYS);
+    }
+}
+
+static void SDLCALL
+SDL_Convert_F32_to_S32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+{
+    const float *src = (const float *) cvt->buf;
+    Sint32 *dst = (Sint32 *) cvt->buf;
+    int i;
+
+    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S32 (using NEON)");
+
+    /* Get dst aligned to 16 bytes */
+    for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = -2147483648;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
+    }
+
+    SDL_assert(!i || ((((size_t) dst) & 15) == 0));
+    SDL_assert(!i || ((((size_t) src) & 15) == 0));
+
+    {
+        /* Aligned! Do NEON blocks as long as we have 16 bytes available. */
+        const float32x4_t one = vdupq_n_f32(1.0f);
+        const float32x4_t negone = vdupq_n_f32(-1.0f);
+        const float32x4_t mulby8388607 = vdupq_n_f32(8388607.0f);
+        int32_t *mmdst = (int32_t *) dst;
+        while (i >= 4) {   /* 4 * float32 */
+            vst1q_s32(mmdst, vshlq_n_s32(vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby8388607)), 8));
+            i -= 4; src += 4; mmdst += 4;
+        }
+        dst = (Sint32 *) mmdst;
+    }
+
+    /* Finish off any leftovers with scalar operations. */
+    while (i) {
+        const float sample = *src;
+        if (sample >= 1.0f) {
+            *dst = 2147483647;
+        } else if (sample <= -1.0f) {
+            *dst = -2147483648;
+        } else {
+            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
+        }
+        i--; src++; dst++;
+    }
+
+    if (cvt->filters[++cvt->filter_index]) {
+        cvt->filters[cvt->filter_index](cvt, AUDIO_S32SYS);
+    }
+}
+#endif
+
 
 
 void SDL_ChooseAudioConverters(void)
@@ -817,6 +1412,13 @@
     }
 #endif
 
+#if HAVE_NEON_INTRINSICS
+    if (SDL_HasNEON()) {
+        SET_CONVERTER_FUNCS(NEON);
+        return;
+    }
+#endif
+
 #if NEED_SCALAR_CONVERTER_FALLBACKS
     SET_CONVERTER_FUNCS(Scalar);
 #endif
diff --git a/source/src/audio/SDL_sysaudio.h b/source/src/audio/SDL_sysaudio.h
index f0e1f3d..579dea5 100644
--- a/source/src/audio/SDL_sysaudio.h
+++ b/source/src/audio/SDL_sysaudio.h
@@ -98,8 +98,10 @@
 typedef struct SDL_AudioDeviceItem
 {
     void *handle;
+    char *name;
+    char *original_name;
+    int dupenum;
     struct SDL_AudioDeviceItem *next;
-    char name[SDL_VARIABLE_LENGTH_ARRAY];
 } SDL_AudioDeviceItem;
 
 
diff --git a/source/src/audio/alsa/SDL_alsa_audio.c b/source/src/audio/alsa/SDL_alsa_audio.c
index 2dba1ff..eff192b 100644
--- a/source/src/audio/alsa/SDL_alsa_audio.c
+++ b/source/src/audio/alsa/SDL_alsa_audio.c
@@ -22,6 +22,10 @@
 
 #if SDL_AUDIO_DRIVER_ALSA
 
+#ifndef SDL_ALSA_NON_BLOCKING
+#define SDL_ALSA_NON_BLOCKING 0
+#endif
+
 /* Allow access to a raw mixing buffer */
 
 #include <sys/types.h>
@@ -90,6 +94,7 @@
 static int (*ALSA_snd_device_name_hint) (int, const char *, void ***);
 static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *);
 static int (*ALSA_snd_device_name_free_hint) (void **);
+static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
 #ifdef SND_CHMAP_API_VERSION
 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
 static int (*ALSA_snd_pcm_chmap_print) (const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
@@ -158,6 +163,7 @@
     SDL_ALSA_SYM(snd_device_name_hint);
     SDL_ALSA_SYM(snd_device_name_get_hint);
     SDL_ALSA_SYM(snd_device_name_free_hint);
+    SDL_ALSA_SYM(snd_pcm_avail);
 #ifdef SND_CHMAP_API_VERSION
     SDL_ALSA_SYM(snd_pcm_get_chmap);
     SDL_ALSA_SYM(snd_pcm_chmap_print);
@@ -243,7 +249,24 @@
 static void
 ALSA_WaitDevice(_THIS)
 {
-    /* We're in blocking mode, so there's nothing to do here */
+#if SDL_ALSA_NON_BLOCKING
+    const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->spec.samples;
+    while (SDL_AtomicGet(&this->enabled)) {
+        const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle);
+        if ((rc < 0) && (rc != -EAGAIN)) {
+            /* Hmm, not much we can do - abort */
+            fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
+                        ALSA_snd_strerror(rc));
+            SDL_OpenedAudioDeviceDisconnected(this);
+            return;
+        } else if (rc < needed) {
+            const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / this->spec.freq;
+            SDL_Delay(SDL_max(delay, 10));
+        } else {
+            break;  /* ready to go! */
+        }
+    }
+#endif
 }
 
 
@@ -422,7 +445,7 @@
 ALSA_CloseDevice(_THIS)
 {
     if (this->hidden->pcm_handle) {
-	/* Wait for the submitted audio to drain
+        /* Wait for the submitted audio to drain
            ALSA_snd_pcm_drop() can hang, so don't use that.
          */
         Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2;
@@ -435,10 +458,32 @@
 }
 
 static int
-ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
+ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params)
 {
     int status;
+    snd_pcm_hw_params_t *hwparams;
     snd_pcm_uframes_t bufsize;
+    snd_pcm_uframes_t persize;
+
+    /* Copy the hardware parameters for this setup */
+    snd_pcm_hw_params_alloca(&hwparams);
+    ALSA_snd_pcm_hw_params_copy(hwparams, params);
+
+    /* Prioritize matching the period size to the requested buffer size */
+    persize = this->spec.samples;
+    status = ALSA_snd_pcm_hw_params_set_period_size_near(
+                this->hidden->pcm_handle, hwparams, &persize, NULL);
+    if ( status < 0 ) {
+        return(-1);
+    }
+
+    /* Next try to restrict the parameters to having only two periods */
+    bufsize = this->spec.samples * 2;
+    status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
+                    this->hidden->pcm_handle, hwparams, &bufsize);
+    if ( status < 0 ) {
+        return(-1);
+    }
 
     /* "set" the hardware with the desired parameters */
     status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
@@ -446,24 +491,12 @@
         return(-1);
     }
 
-    /* Get samples for the actual buffer size */
-    status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
-    if ( status < 0 ) {
-        return(-1);
-    }
-    if ( !override && bufsize != this->spec.samples * 2 ) {
-        return(-1);
-    }
-
-    /* !!! FIXME: Is this safe to do? */
-    this->spec.samples = bufsize / 2;
+    this->spec.samples = persize;
 
     /* This is useful for debugging */
     if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
-        snd_pcm_uframes_t persize = 0;
         unsigned int periods = 0;
 
-        ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
         ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
 
         fprintf(stderr,
@@ -472,78 +505,6 @@
     }
 
     return(0);
-}
-
-static int
-ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
-{
-    const char *env;
-    int status;
-    snd_pcm_hw_params_t *hwparams;
-    snd_pcm_uframes_t frames;
-    unsigned int periods;
-
-    /* Copy the hardware parameters for this setup */
-    snd_pcm_hw_params_alloca(&hwparams);
-    ALSA_snd_pcm_hw_params_copy(hwparams, params);
-
-    if ( !override ) {
-        env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
-        if ( env ) {
-            override = SDL_atoi(env);
-            if ( override == 0 ) {
-                return(-1);
-            }
-        }
-    }
-
-    frames = this->spec.samples;
-    status = ALSA_snd_pcm_hw_params_set_period_size_near(
-                this->hidden->pcm_handle, hwparams, &frames, NULL);
-    if ( status < 0 ) {
-        return(-1);
-    }
-
-    periods = 2;
-    status = ALSA_snd_pcm_hw_params_set_periods_near(
-                this->hidden->pcm_handle, hwparams, &periods, NULL);
-    if ( status < 0 ) {
-        return(-1);
-    }
-
-    return ALSA_finalize_hardware(this, hwparams, override);
-}
-
-static int
-ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
-{
-    const char *env;
-    int status;
-    snd_pcm_hw_params_t *hwparams;
-    snd_pcm_uframes_t frames;
-
-    /* Copy the hardware parameters for this setup */
-    snd_pcm_hw_params_alloca(&hwparams);
-    ALSA_snd_pcm_hw_params_copy(hwparams, params);
-
-    if ( !override ) {
-        env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
-        if ( env ) {
-            override = SDL_atoi(env);
-            if ( override == 0 ) {
-                return(-1);
-            }
-        }
-    }
-
-    frames = this->spec.samples * 2;
-    status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
-                    this->hidden->pcm_handle, hwparams, &frames);
-    if ( status < 0 ) {
-        return(-1);
-    }
-
-    return ALSA_finalize_hardware(this, hwparams, override);
 }
 
 static int
@@ -692,14 +653,11 @@
     this->spec.freq = rate;
 
     /* Set the buffer size, in samples */
-    if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
-         ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
-        /* Failed to set desired buffer size, do the best you can... */
-        status = ALSA_set_period_size(this, hwparams, 1);
-        if (status < 0) {
-            return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
-        }
+    status = ALSA_set_buffer_size(this, hwparams);
+    if (status < 0) {
+        return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
     }
+
     /* Set the software parameters */
     snd_pcm_sw_params_alloca(&swparams);
     status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
@@ -737,9 +695,11 @@
         SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
     }
 
+    #if !SDL_ALSA_NON_BLOCKING
     if (!iscapture) {
         ALSA_snd_pcm_nonblock(pcm_handle, 0);
     }
+    #endif
 
     /* We're ready to rock and roll. :-) */
     return 0;
diff --git a/source/src/audio/android/SDL_androidaudio.c b/source/src/audio/android/SDL_androidaudio.c
index 7a25424..77a5f0d 100644
--- a/source/src/audio/android/SDL_androidaudio.c
+++ b/source/src/audio/android/SDL_androidaudio.c
@@ -57,7 +57,9 @@
 
     test_format = SDL_FirstAudioFormat(this->spec.format);
     while (test_format != 0) { /* no "UNKNOWN" constant */
-        if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) {
+        if ((test_format == AUDIO_U8) ||
+			(test_format == AUDIO_S16) ||
+			(test_format == AUDIO_F32)) {
             this->spec.format = test_format;
             break;
         }
@@ -69,25 +71,8 @@
         return SDL_SetError("No compatible audio format!");
     }
 
-    if (this->spec.channels > 1) {
-        this->spec.channels = 2;
-    } else {
-        this->spec.channels = 1;
-    }
-
-    if (this->spec.freq < 8000) {
-        this->spec.freq = 8000;
-    }
-    if (this->spec.freq > 48000) {
-        this->spec.freq = 48000;
-    }
-
-    /* TODO: pass in/return a (Java) device ID */
-    this->spec.samples = Android_JNI_OpenAudioDevice(iscapture, this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
-
-    if (this->spec.samples == 0) {
-        /* Init failed? */
-        return SDL_SetError("Java-side initialization failed!");
+    if (Android_JNI_OpenAudioDevice(iscapture, &this->spec) < 0) {
+        return -1;
     }
 
     SDL_CalculateAudioSpec(&this->spec);
diff --git a/source/src/audio/arts/SDL_artsaudio.c b/source/src/audio/arts/SDL_artsaudio.c
index 4e3ebf2..47bad4b 100644
--- a/source/src/audio/arts/SDL_artsaudio.c
+++ b/source/src/audio/arts/SDL_artsaudio.c
@@ -39,7 +39,7 @@
 #include "SDL_name.h"
 #include "SDL_loadso.h"
 #else
-#define SDL_NAME(X)	X
+#define SDL_NAME(X) X
 #endif
 
 #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
diff --git a/source/src/audio/coreaudio/SDL_coreaudio.h b/source/src/audio/coreaudio/SDL_coreaudio.h
index 7ce8b8d..dcce3f7 100644
--- a/source/src/audio/coreaudio/SDL_coreaudio.h
+++ b/source/src/audio/coreaudio/SDL_coreaudio.h
@@ -45,16 +45,14 @@
 
 struct SDL_PrivateAudioData
 {
-    SDL_Thread *thread;
     AudioQueueRef audioQueue;
+    int numAudioBuffers;
     AudioQueueBufferRef *audioBuffer;
     void *buffer;
-    UInt32 bufferOffset;
     UInt32 bufferSize;
     AudioStreamBasicDescription strdesc;
-    SDL_sem *ready_semaphore;
-    char *thread_error;
-    SDL_atomic_t shutdown;
+    SDL_bool refill;
+    SDL_AudioStream *capturestream;
 #if MACOSX_COREAUDIO
     AudioDeviceID deviceID;
 #else
diff --git a/source/src/audio/coreaudio/SDL_coreaudio.m b/source/src/audio/coreaudio/SDL_coreaudio.m
index 92f5f12..59242f9 100644
--- a/source/src/audio/coreaudio/SDL_coreaudio.m
+++ b/source/src/audio/coreaudio/SDL_coreaudio.m
@@ -26,6 +26,7 @@
 
 #include "SDL_audio.h"
 #include "SDL_hints.h"
+#include "SDL_timer.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_sysaudio.h"
 #include "SDL_coreaudio.h"
@@ -354,7 +355,7 @@
             return NO;
         }
 
-        if (open_playback_devices + open_capture_devices == 1) {
+        if (open && (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);
@@ -391,10 +392,10 @@
             if (this->hidden->interruption_listener != NULL) {
                 SDLInterruptionListener *listener = nil;
                 listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
+                [center removeObserver:listener];
                 @synchronized (listener) {
                     listener.device = NULL;
                 }
-                [center removeObserver:listener];
             }
         }
     }
@@ -409,43 +410,27 @@
 outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
 {
     SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
-    if (SDL_AtomicGet(&this->hidden->shutdown)) {
-        return;  /* don't do anything. */
-    }
-
-    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
-        /* Supply silence if audio is not enabled or paused */
-        SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
-    } else {
-        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
-        Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
-
-        while (remaining > 0) {
-            UInt32 len;
-            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
-                /* Generate the data */
-                SDL_LockMutex(this->mixer_lock);
-                (*this->callbackspec.callback)(this->callbackspec.userdata,
-                            this->hidden->buffer, this->hidden->bufferSize);
-                SDL_UnlockMutex(this->mixer_lock);
-                this->hidden->bufferOffset = 0;
-            }
-
-            len = this->hidden->bufferSize - this->hidden->bufferOffset;
-            if (len > remaining) {
-                len = remaining;
-            }
-            SDL_memcpy(ptr, (char *)this->hidden->buffer +
-                       this->hidden->bufferOffset, len);
-            ptr = ptr + len;
-            remaining -= len;
-            this->hidden->bufferOffset += len;
-        }
-    }
-
+    SDL_assert(inBuffer->mAudioDataBytesCapacity == this->hidden->bufferSize);
+    SDL_memcpy(inBuffer->mAudioData, this->hidden->buffer, this->hidden->bufferSize);
+    SDL_memset(this->hidden->buffer, '\0', this->hidden->bufferSize);  /* zero out in case we have to fill again without new data. */
+    inBuffer->mAudioDataByteSize = this->hidden->bufferSize;
     AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+    this->hidden->refill = SDL_TRUE;
+}
 
-    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
+static Uint8 *
+COREAUDIO_GetDeviceBuf(_THIS)
+{
+    return this->hidden->buffer;
+}
+
+static void
+COREAUDIO_WaitDevice(_THIS)
+{
+    while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) {
+        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
+    }
+    this->hidden->refill = SDL_FALSE;
 }
 
 static void
@@ -454,36 +439,46 @@
               const AudioStreamPacketDescription *inPacketDescs )
 {
     SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
-
-    if (SDL_AtomicGet(&this->shutdown)) {
-        return;  /* don't do anything. */
-    }
-
-    /* ignore unless we're active. */
-    if (!SDL_AtomicGet(&this->paused) && SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {
-        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->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
-                SDL_UnlockMutex(this->mixer_lock);
-                this->hidden->bufferOffset = 0;
-            }
+    if (SDL_AtomicGet(&this->enabled)) {
+        SDL_AudioStream *stream = this->hidden->capturestream;
+        if (SDL_AudioStreamPut(stream, inBuffer->mAudioData, inBuffer->mAudioDataByteSize) == -1) {
+            /* yikes, out of memory or something. I guess drop the buffer. Our WASAPI target kills the device in this case, though */
         }
+        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+        this->hidden->refill = SDL_TRUE;
+    }
+}
+
+static int
+COREAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    SDL_AudioStream *stream = this->hidden->capturestream;
+    while (SDL_AtomicGet(&this->enabled)) {
+        const int avail = SDL_AudioStreamAvailable(stream);
+        if (avail > 0) {
+            const int cpy = SDL_min(buflen, avail);
+            SDL_AudioStreamGet(stream, buffer, cpy);
+            return cpy;
+        }
+
+        /* wait for more data, try again. */
+        while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) {
+            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
+        }
+        this->hidden->refill = SDL_FALSE;
     }
 
-    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+    return 0;  /* not enabled, giving up. */
+}
+
+static void
+COREAUDIO_FlushCapture(_THIS)
+{
+    while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1) == kCFRunLoopRunHandledSource) {
+        /* spin. */
+    }
+    this->hidden->refill = SDL_FALSE;
+    SDL_AudioStreamClear(this->hidden->capturestream);
 }
 
 
@@ -541,25 +536,16 @@
     update_audio_session(this, SDL_FALSE);
 #endif
 
-    /* if callback fires again, feed silence; don't call into the app. */
-    SDL_AtomicSet(&this->paused, 1);
-
     if (this->hidden->audioQueue) {
         AudioQueueDispose(this->hidden->audioQueue, 1);
     }
 
-    if (this->hidden->thread) {
-        SDL_AtomicSet(&this->hidden->shutdown, 1);
-        SDL_WaitThread(this->hidden->thread, NULL);
-    }
-
-    if (this->hidden->ready_semaphore) {
-        SDL_DestroySemaphore(this->hidden->ready_semaphore);
+    if (this->hidden->capturestream) {
+        SDL_FreeAudioStream(this->hidden->capturestream);
     }
 
     /* AudioQueueDispose() frees the actual buffer objects. */
     SDL_free(this->hidden->audioBuffer);
-    SDL_free(this->hidden->thread_error);
     SDL_free(this->hidden->buffer);
     SDL_free(this->hidden);
 
@@ -625,6 +611,8 @@
 }
 #endif
 
+
+/* this all happens in the audio thread, since it needs a separate runloop. */
 static int
 prepare_audioqueue(_THIS)
 {
@@ -664,19 +652,6 @@
 }
 #endif
 
-    /* Calculate the final parameters for this audio specification */
-    SDL_CalculateAudioSpec(&this->spec);
-
-    /* Allocate a sample buffer */
-    this->hidden->bufferSize = this->spec.size;
-    this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
-
-    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
-    if (this->hidden->buffer == NULL) {
-        SDL_OutOfMemory();
-        return 0;
-    }
-
     /* Make sure we can feed the device a minimum amount of time */
     double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
 #if defined(__IPHONEOS__)
@@ -691,6 +666,7 @@
         numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
     }
 
+    this->hidden->numAudioBuffers = numAudioBuffers;
     this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
     if (this->hidden->audioBuffer == NULL) {
         SDL_OutOfMemory();
@@ -717,29 +693,23 @@
     return 1;
 }
 
-static int
-audioqueue_thread(void *arg)
+static void
+COREAUDIO_ThreadInit(_THIS)
 {
-    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;
+        /* !!! FIXME: do this in RunAudio, and maybe block OpenDevice until ThreadInit finishes, too, to report an opening error */
+        SDL_OpenedAudioDeviceDisconnected(this);  /* oh well. */
     }
+}
 
-    /* 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) {  /* Drain off any pending playback. */
-        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 void
+COREAUDIO_PrepareToClose(_THIS)
+{
+    /* run long enough to queue some silence, so we know our actual audio
+       has been played */
+    CFRunLoopRunInMode(kCFRunLoopDefaultMode, (((this->spec.samples * 1000) / this->spec.freq) * 2) / 1000.0f, 0);
+    AudioQueueStop(this->hidden->audioQueue, 1);
 }
 
 static int
@@ -826,28 +796,23 @@
     }
 #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. */
+    /* Calculate the final parameters for this audio specification */
+    SDL_CalculateAudioSpec(&this->spec);
+
+    if (iscapture) {
+        this->hidden->capturestream = SDL_NewAudioStream(this->spec.format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
+        if (!this->hidden->capturestream) {
+            return -1;  /* already set SDL_Error */
+        }
+    } else {
+        this->hidden->bufferSize = this->spec.size;
+        this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
+        if (this->hidden->buffer == NULL) {
+            return SDL_OutOfMemory();
+        }
     }
 
-    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;
+    return 0;
 }
 
 static void
@@ -867,6 +832,12 @@
     impl->OpenDevice = COREAUDIO_OpenDevice;
     impl->CloseDevice = COREAUDIO_CloseDevice;
     impl->Deinitialize = COREAUDIO_Deinitialize;
+    impl->ThreadInit = COREAUDIO_ThreadInit;
+    impl->WaitDevice = COREAUDIO_WaitDevice;
+    impl->GetDeviceBuf = COREAUDIO_GetDeviceBuf;
+    impl->PrepareToClose = COREAUDIO_PrepareToClose;
+    impl->CaptureFromDevice = COREAUDIO_CaptureFromDevice;
+    impl->FlushCapture = COREAUDIO_FlushCapture;
 
 #if MACOSX_COREAUDIO
     impl->DetectDevices = COREAUDIO_DetectDevices;
@@ -876,7 +847,6 @@
     impl->OnlyHasDefaultCaptureDevice = 1;
 #endif
 
-    impl->ProvidesOwnCallbackThread = 1;
     impl->HasCaptureSupport = 1;
 
     return 1;   /* this audio target is available. */
diff --git a/source/src/audio/directsound/SDL_directsound.c b/source/src/audio/directsound/SDL_directsound.c
index 09b83ae..a943ba2 100644
--- a/source/src/audio/directsound/SDL_directsound.c
+++ b/source/src/audio/directsound/SDL_directsound.c
@@ -477,8 +477,8 @@
     SDL_bool tried_format = SDL_FALSE;
     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
     LPGUID guid = (LPGUID) handle;
-	DWORD bufsize;
-	
+    DWORD bufsize;
+
     /* Initialize all variables that we clean on shutdown */
     this->hidden = (struct SDL_PrivateAudioData *)
         SDL_malloc((sizeof *this->hidden));
@@ -526,7 +526,7 @@
                              (int) (DSBSIZE_MAX / numchunks));
             } else {
                 int rc;
-				WAVEFORMATEX wfmt;
+                WAVEFORMATEX wfmt;
                 SDL_zero(wfmt);
                 if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
                     wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
diff --git a/source/src/audio/jack/SDL_jackaudio.c b/source/src/audio/jack/SDL_jackaudio.c
index a252da7..76ff431 100644
--- a/source/src/audio/jack/SDL_jackaudio.c
+++ b/source/src/audio/jack/SDL_jackaudio.c
@@ -44,7 +44,9 @@
 static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *);
 static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *);
 static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long);
+static jack_port_t * (*JACK_jack_port_by_name) (jack_client_t *, const char *);
 static const char * (*JACK_jack_port_name) (const jack_port_t *);
+static const char * (*JACK_jack_port_type) (const jack_port_t *);
 static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *);
 static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *);
 
@@ -135,7 +137,9 @@
     SDL_JACK_SYM(jack_get_sample_rate);
     SDL_JACK_SYM(jack_get_buffer_size);
     SDL_JACK_SYM(jack_port_register);
+    SDL_JACK_SYM(jack_port_by_name);
     SDL_JACK_SYM(jack_port_name);
+    SDL_JACK_SYM(jack_port_type);
     SDL_JACK_SYM(jack_connect);
     SDL_JACK_SYM(jack_set_process_callback);
     return 0;
@@ -273,10 +277,6 @@
         SDL_DestroySemaphore(this->hidden->iosem);
     }
 
-    if (this->hidden->devports) {
-        JACK_jack_free(this->hidden->devports);
-    }
-
     SDL_free(this->hidden->iobuffer);
 }
 
@@ -292,9 +292,11 @@
     const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
     const char *sdlportstr = iscapture ? "input" : "output";
     const char **devports = NULL;
+    int *audio_ports;
     jack_client_t *client = NULL;
     jack_status_t status;
     int channels = 0;
+    int ports = 0;
     int i;
 
     /* Initialize all variables that we clean on shutdown */
@@ -311,14 +313,29 @@
     }
 
     devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
-    this->hidden->devports = devports;
     if (!devports || !devports[0]) {
         return SDL_SetError("No physical JACK ports available");
     }
 
-    while (devports[++channels]) {
+    while (devports[++ports]) {
         /* spin to count devports */
     }
+
+    /* Filter out non-audio ports */
+    audio_ports = SDL_calloc(ports, sizeof *audio_ports);
+    for (i = 0; i < ports; i++) {
+        const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]);
+        const char *type = JACK_jack_port_type(dport);
+        const int len = SDL_strlen(type);
+        /* See if type ends with "audio" */
+        if (len >= 5 && !SDL_memcmp(type+len-5, "audio", 5)) {
+            audio_ports[channels++] = i;
+        }
+    }
+    if (channels == 0) {
+        return SDL_SetError("No physical JACK ports available");
+    }
+
 
     /* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */
 
@@ -368,16 +385,16 @@
     /* once activated, we can connect all the ports. */
     for (i = 0; i < channels; i++) {
         const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]);
-        const char *srcport = iscapture ? devports[i] : sdlport;
-        const char *dstport = iscapture ? sdlport : devports[i];
+        const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
+        const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
         if (JACK_jack_connect(client, srcport, dstport) != 0) {
             return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
         }
     }
 
     /* don't need these anymore. */
-    this->hidden->devports = NULL;
     JACK_jack_free(devports);
+    SDL_free(audio_ports);
 
     /* We're ready to rock and roll. :-) */
     return 0;
diff --git a/source/src/audio/jack/SDL_jackaudio.h b/source/src/audio/jack/SDL_jackaudio.h
index aab199a..5bc04bd 100644
--- a/source/src/audio/jack/SDL_jackaudio.h
+++ b/source/src/audio/jack/SDL_jackaudio.h
@@ -33,7 +33,6 @@
     jack_client_t *client;
     SDL_sem *iosem;
     float *iobuffer;
-    const char **devports;
     jack_port_t **sdlports;
 };
 
diff --git a/source/src/audio/pulseaudio/SDL_pulseaudio.c b/source/src/audio/pulseaudio/SDL_pulseaudio.c
index 1e98580..053a1c3 100644
--- a/source/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/source/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -109,7 +109,7 @@
     pa_stream_success_cb_t, void *);
 static int (*PULSEAUDIO_pa_stream_peek) (pa_stream *, const void **, size_t *);
 static int (*PULSEAUDIO_pa_stream_drop) (pa_stream *);
-static pa_operation * (*PULSEAUDIO_pa_stream_flush)	(pa_stream *,
+static pa_operation * (*PULSEAUDIO_pa_stream_flush) (pa_stream *,
     pa_stream_success_cb_t, void *);
 static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
 static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
diff --git a/source/src/audio/wasapi/SDL_wasapi.c b/source/src/audio/wasapi/SDL_wasapi.c
index b7c8dda..f517539 100644
--- a/source/src/audio/wasapi/SDL_wasapi.c
+++ b/source/src/audio/wasapi/SDL_wasapi.c
@@ -725,6 +725,12 @@
     WASAPI_PlatformThreadDeinit(this);
 }
 
+void
+WASAPI_BeginLoopIteration(_THIS)
+{
+	/* no-op. */
+}
+
 static void
 WASAPI_Deinitialize(void)
 {
diff --git a/source/src/audio/wasapi/SDL_wasapi_win32.c b/source/src/audio/wasapi/SDL_wasapi_win32.c
index 8b55582..9d7c159 100644
--- a/source/src/audio/wasapi/SDL_wasapi_win32.c
+++ b/source/src/audio/wasapi/SDL_wasapi_win32.c
@@ -351,10 +351,42 @@
 }
 
 
+typedef struct
+{
+    LPWSTR devid;
+    char *devname;
+} EndpointItem;
+
+static int sort_endpoints(const void *_a, const void *_b)
+{
+    LPWSTR a = ((const EndpointItem *) _a)->devid;
+    LPWSTR b = ((const EndpointItem *) _b)->devid;
+    if (!a && b) {
+        return -1;
+    } else if (a && !b) {
+        return 1;
+    }
+
+    while (SDL_TRUE) {
+        if (*a < *b) {
+            return -1;
+        } else if (*a > *b) {
+            return 1;
+        } else if (*a == 0) {
+            break;
+        }
+        a++;
+        b++;
+    }
+
+    return 0;
+}
+
 static void
 WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture)
 {
     IMMDeviceCollection *collection = NULL;
+    EndpointItem *items;
     UINT i, total;
 
     /* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
@@ -369,22 +401,36 @@
         return;
     }
 
+    items = (EndpointItem *) SDL_calloc(total, sizeof (EndpointItem));
+    if (!items) {
+        return;  /* oh well. */
+    }
+
     for (i = 0; i < total; i++) {
+        EndpointItem *item = items + i;
         IMMDevice *device = NULL;
         if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
-            LPWSTR devid = NULL;
-            if (SUCCEEDED(IMMDevice_GetId(device, &devid))) {
-                char *devname = GetWasapiDeviceName(device);
-                if (devname) {
-                    WASAPI_AddDevice(iscapture, devname, devid);
-                    SDL_free(devname);
-                }
-                CoTaskMemFree(devid);
+            if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
+                item->devname = GetWasapiDeviceName(device);
             }
             IMMDevice_Release(device);
         }
     }
 
+    /* sort the list of devices by their guid so list is consistent between runs */
+    SDL_qsort(items, total, sizeof (*items), sort_endpoints);
+
+    /* Send the sorted list on to the SDL's higher level. */
+    for (i = 0; i < total; i++) {
+        EndpointItem *item = items + i;
+        if ((item->devid) && (item->devname)) {
+            WASAPI_AddDevice(iscapture, item->devname, item->devid);
+        }
+        SDL_free(item->devname);
+        CoTaskMemFree(item->devid);
+    }
+
+    SDL_free(items);
     IMMDeviceCollection_Release(collection);
 }
 
@@ -403,12 +449,6 @@
 {
     /* not asynchronous. */
     SDL_assert(!"This function should have only been called on WinRT.");
-}
-
-void
-WASAPI_BeginLoopIteration(_THIS)
-{
-    /* no-op. */
 }
 
 #endif  /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */
diff --git a/source/src/audio/wasapi/SDL_wasapi_winrt.cpp b/source/src/audio/wasapi/SDL_wasapi_winrt.cpp
index 309ec6a..2ca09de 100644
--- a/source/src/audio/wasapi/SDL_wasapi_winrt.cpp
+++ b/source/src/audio/wasapi/SDL_wasapi_winrt.cpp
@@ -185,20 +185,9 @@
 HRESULT
 SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
 {
-    HRESULT result = S_OK;
-    IUnknown *iunknown = nullptr;
-    const HRESULT ret = async->GetActivateResult(&result, &iunknown);
-
-    if (SUCCEEDED(ret) && SUCCEEDED(result)) {
-        iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client));
-        if (device->hidden->client) {
-            // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
-            SDL_AtomicSet(&device->hidden->just_activated, 1);
-        }
-    }
-
+    // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
+    SDL_AtomicSet(&device->hidden->just_activated, 1);
     WASAPI_UnrefDevice(device);
-
     return S_OK;
 }
 
@@ -236,27 +225,47 @@
     IActivateAudioInterfaceAsyncOperation *async = nullptr;
     const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
 
-    if (async != nullptr) {
-        async->Release();
-    }
-
-    if (FAILED(ret)) {
+    if (FAILED(ret) || async == nullptr) {
+        if (async != nullptr) {
+            async->Release();
+        }
         handler.Get()->Release();
         WASAPI_UnrefDevice(_this);
         return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
     }
 
-    return 0;
-}
-
-void
-WASAPI_BeginLoopIteration(_THIS)
-{
-    if (SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
-        if (WASAPI_PrepDevice(_this, SDL_TRUE) == -1) {
-            SDL_OpenedAudioDeviceDisconnected(_this);
-        } 
+    /* Spin until the async operation is complete.
+     * If we don't PrepDevice before leaving this function, the bug list gets LONG:
+     * - device.spec is not filled with the correct information
+     * - The 'obtained' spec will be wrong for ALLOW_CHANGE properties
+     * - SDL_AudioStreams will/will not be allocated at the right time
+     * - SDL_assert(device->callbackspec.size == device->spec.size) will fail
+     * - When the assert is ignored, skipping or a buffer overflow will occur
+     */
+    while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
+        SDL_Delay(1);
     }
+
+    HRESULT activateRes = S_OK;
+    IUnknown *iunknown = nullptr;
+    const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown);
+    async->Release();
+    if (FAILED(getActivateRes)) {
+        return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes);
+    } else if (FAILED(activateRes)) {
+        return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
+    }
+
+    iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client));
+    if (!_this->hidden->client) {
+        return SDL_SetError("Failed to query WASAPI client interface");
+    }
+
+    if (WASAPI_PrepDevice(_this, isrecovery) == -1) {
+        return -1;
+    }
+
+    return 0;
 }
 
 void
diff --git a/source/src/audio/winmm/SDL_winmm.c b/source/src/audio/winmm/SDL_winmm.c
index 8e5c17b..20426f1 100644
--- a/source/src/audio/winmm/SDL_winmm.c
+++ b/source/src/audio/winmm/SDL_winmm.c
@@ -78,7 +78,7 @@
     capstyp##2W caps; \
     UINT i; \
     for (i = 0; i < devcount; i++) { \
-	if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
+        if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
             char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \
             if (name != NULL) { \
                 SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \
@@ -375,8 +375,7 @@
 #endif
 
     /* Create the audio buffer semaphore */
-    this->hidden->audio_sem =
-		CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
+    this->hidden->audio_sem = CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
     if (this->hidden->audio_sem == NULL) {
         return SDL_SetError("Couldn't create semaphore");
     }
diff --git a/source/src/core/android/SDL_android.c b/source/src/core/android/SDL_android.c
index c40c676..a56575e 100644
--- a/source/src/core/android/SDL_android.c
+++ b/source/src/core/android/SDL_android.c
@@ -61,6 +61,10 @@
 #define SDL_JAVA_CONTROLLER_INTERFACE(function)         CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
 #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function)   CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
 
+/* Audio encoding definitions */
+#define ENCODING_PCM_8BIT   3
+#define ENCODING_PCM_16BIT  2
+#define ENCODING_PCM_FLOAT  4
 
 /* Java class SDLActivity */
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
@@ -76,7 +80,8 @@
 
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
         JNIEnv* env, jclass jcls,
-        jint width, jint height, jint format, jfloat rate);
+        jint surfaceWidth, jint surfaceHeight,
+        jint deviceWidth, jint deviceHeight, jint format, jfloat rate);
 
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
         JNIEnv* env, jclass jcls);
@@ -102,7 +107,7 @@
 
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
         JNIEnv* env, jclass jcls,
-        jint button, jint action, jfloat x, jfloat y);
+        jint button, jint action, jfloat x, jfloat y, jboolean relative);
 
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
         JNIEnv* env, jclass jcls,
@@ -134,10 +139,18 @@
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)(
         JNIEnv* env, jclass cls);
 
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
+        JNIEnv* env, jclass cls,
+        jint orientation);
+
 /* Java class SDLInputConnection */
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
         JNIEnv* env, jclass cls,
         jstring text, jint newCursorPosition);
+
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
+        JNIEnv* env, jclass cls,
+        jchar chUnicode);
 
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
         JNIEnv* env, jclass cls,
@@ -169,8 +182,8 @@
 
 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
         JNIEnv* env, jclass jcls,
-        jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
-        jint nbuttons, jint naxes, jint nhats, jint nballs);
+        jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
+        jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs);
 
 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
         JNIEnv* env, jclass jcls,
@@ -190,6 +203,7 @@
 /* #define DEBUG_JNI */
 
 static void Android_JNI_ThreadDestroyed(void*);
+static void checkJNIReady(void);
 
 /*******************************************************************************
  This file links the Java side of Android with libsdl
@@ -212,7 +226,11 @@
 static jmethodID midSetWindowStyle;
 static jmethodID midSetOrientation;
 static jmethodID midGetContext;
+static jmethodID midIsTablet;
 static jmethodID midIsAndroidTV;
+static jmethodID midIsChromebook;
+static jmethodID midIsDeXMode;
+static jmethodID midManualBackButton;
 static jmethodID midInputGetInputDeviceIds;
 static jmethodID midSendMessage;
 static jmethodID midShowTextInput;
@@ -223,18 +241,25 @@
 static jmethodID midOpenAPKExpansionInputStream;
 static jmethodID midGetManifestEnvironmentVariables;
 static jmethodID midGetDisplayDPI;
+static jmethodID midCreateCustomCursor;
+static jmethodID midSetCustomCursor;
+static jmethodID midSetSystemCursor;
+static jmethodID midSupportsRelativeMouse;
+static jmethodID midSetRelativeMouseEnabled;
 
 /* audio manager */
 static jclass mAudioManagerClass;
 
 /* method signatures */
 static jmethodID midAudioOpen;
-static jmethodID midAudioWriteShortBuffer;
 static jmethodID midAudioWriteByteBuffer;
+static jmethodID midAudioWriteShortBuffer;
+static jmethodID midAudioWriteFloatBuffer;
 static jmethodID midAudioClose;
 static jmethodID midCaptureOpen;
-static jmethodID midCaptureReadShortBuffer;
 static jmethodID midCaptureReadByteBuffer;
+static jmethodID midCaptureReadShortBuffer;
+static jmethodID midCaptureReadFloatBuffer;
 static jmethodID midCaptureClose;
 
 /* controller manager */
@@ -244,6 +269,7 @@
 static jmethodID midPollInputDevices;
 static jmethodID midPollHapticDevices;
 static jmethodID midHapticRun;
+static jmethodID midHapticStop;
 
 /* static fields */
 static jfieldID fidSeparateMouseAndTouch;
@@ -309,8 +335,16 @@
                                 "setOrientation","(IIZLjava/lang/String;)V");
     midGetContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "getContext","()Landroid/content/Context;");
+    midIsTablet = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "isTablet", "()Z");
     midIsAndroidTV = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "isAndroidTV","()Z");
+    midIsChromebook = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "isChromebook", "()Z");
+    midIsDeXMode = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "isDeXMode", "()Z");
+    midManualBackButton = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
+                                "manualBackButton", "()V");
     midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
                                 "inputGetInputDeviceIds", "(I)[I");
     midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
@@ -332,13 +366,21 @@
                                 "getManifestEnvironmentVariables", "()Z");
 
     midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
-    midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
+    midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createCustomCursor", "([IIIII)I");
+    midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)Z");
+    midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)Z");
+
+    midSupportsRelativeMouse = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "supportsRelativeMouse", "()Z");
+    midSetRelativeMouseEnabled = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
+
 
     if (!midGetNativeSurface ||
-       !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsAndroidTV || !midInputGetInputDeviceIds ||
+       !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInputGetInputDeviceIds ||
        !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
        !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
-       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables|| !midGetDisplayDPI) {
+       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
+       !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
+       !midIsChromebook || !midIsDeXMode || !midManualBackButton) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
     }
 
@@ -361,24 +403,28 @@
     mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
 
     midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
-                                "audioOpen", "(IZZI)I");
-    midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
-                                "audioWriteShortBuffer", "([S)V");
+                                "audioOpen", "(IIII)[I");
     midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
                                 "audioWriteByteBuffer", "([B)V");
+    midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
+                                "audioWriteShortBuffer", "([S)V");
+    midAudioWriteFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
+                                "audioWriteFloatBuffer", "([F)V");
     midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
                                 "audioClose", "()V");
     midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
-                                "captureOpen", "(IZZI)I");
-    midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
-                                "captureReadShortBuffer", "([SZ)I");
+                                "captureOpen", "(IIII)[I");
     midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
                                 "captureReadByteBuffer", "([BZ)I");
+    midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
+                                "captureReadShortBuffer", "([SZ)I");
+    midCaptureReadFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
+                                "captureReadFloatBuffer", "([FZ)I");
     midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
                                 "captureClose", "()V");
 
-    if (!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
-       !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose) {
+    if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
+       !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
     }
 
@@ -399,9 +445,11 @@
     midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
                                 "pollHapticDevices", "()V");
     midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
-                                "hapticRun", "(II)V");
+                                "hapticRun", "(IFI)V");
+    midHapticStop = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
+                                "hapticStop", "(I)V");
 
-    if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun) {
+    if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
     }
 
@@ -503,9 +551,18 @@
 /* Resize */
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
                                     JNIEnv* env, jclass jcls,
-                                    jint width, jint height, jint format, jfloat rate)
+                                    jint surfaceWidth, jint surfaceHeight,
+                                    jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
 {
-    Android_SetScreenResolution(width, height, format, rate);
+    Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
+}
+
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
+                                    JNIEnv *env, jclass jcls,
+                                    jint orientation)
+{
+    SDL_VideoDisplay *display = SDL_GetDisplay(0);
+    SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
 }
 
 /* Paddown */
@@ -543,14 +600,15 @@
 
 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
                                     JNIEnv* env, jclass jcls,
-                                    jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
-                                    jint nbuttons, jint naxes, jint nhats, jint nballs)
+                                    jint device_id, jstring device_name, jstring device_desc,
+                                    jint vendor_id, jint product_id, jboolean is_accelerometer,
+                                    jint button_mask, jint naxes, jint nhats, jint nballs)
 {
     int retval;
     const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
     const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
 
-    retval = Android_AddJoystick(device_id, name, desc, (SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs);
+    retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
 
     (*env)->ReleaseStringUTFChars(env, device_name, name);
     (*env)->ReleaseStringUTFChars(env, device_desc, desc);
@@ -675,9 +733,9 @@
 /* Mouse */
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
                                     JNIEnv* env, jclass jcls,
-                                    jint button, jint action, jfloat x, jfloat y)
+                                    jint button, jint action, jfloat x, jfloat y, jboolean relative)
 {
-    Android_OnMouse(button, action, x, y);
+    Android_OnMouse(button, action, x, y, relative);
 }
 
 /* Accelerometer */
@@ -995,17 +1053,19 @@
 /*
  * Audio support
  */
-static jboolean audioBuffer16Bit = JNI_FALSE;
+static int audioBufferFormat = 0;
 static jobject audioBuffer = NULL;
 static void* audioBufferPinned = NULL;
-static jboolean captureBuffer16Bit = JNI_FALSE;
+static int captureBufferFormat = 0;
 static jobject captureBuffer = NULL;
 
-int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
+int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
 {
-    jboolean audioBufferStereo;
-    int audioBufferFrames;
+    int audioformat;
+    int numBufferFrames;
     jobject jbufobj = NULL;
+    jobject result;
+    int *resultElements;
     jboolean isCopy;
 
     JNIEnv *env = Android_JNI_GetEnv();
@@ -1015,74 +1075,123 @@
     }
     Android_JNI_SetupThread();
 
-    audioBufferStereo = channelCount > 1;
+    switch (spec->format) {
+    case AUDIO_U8:
+        audioformat = ENCODING_PCM_8BIT;
+        break;
+    case AUDIO_S16:
+        audioformat = ENCODING_PCM_16BIT;
+        break;
+    case AUDIO_F32:
+        audioformat = ENCODING_PCM_FLOAT;
+        break;
+    default:
+        return SDL_SetError("Unsupported audio format: 0x%x", spec->format);
+    }
 
     if (iscapture) {
         __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
-        captureBuffer16Bit = is16Bit;
-        if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
-            /* Error during audio initialization */
-            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
-            return 0;
-        }
+        result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples);
     } else {
         __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
-        audioBuffer16Bit = is16Bit;
-        if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
-            /* Error during audio initialization */
-            __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
-            return 0;
-        }
+        result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples);
     }
+    if (result == NULL) {
+        /* Error during audio initialization, error printed from Java */
+        return SDL_SetError("Java-side initialization failed");
+    }
+
+    if ((*env)->GetArrayLength(env, (jintArray)result) != 4) {
+        return SDL_SetError("Unexpected results from Java, expected 4, got %d", (*env)->GetArrayLength(env, (jintArray)result));
+    }
+    isCopy = JNI_FALSE;
+    resultElements = (*env)->GetIntArrayElements(env, (jintArray)result, &isCopy);
+    spec->freq = resultElements[0];
+    audioformat = resultElements[1];
+    switch (audioformat) {
+    case ENCODING_PCM_8BIT:
+        spec->format = AUDIO_U8;
+        break;
+    case ENCODING_PCM_16BIT:
+        spec->format = AUDIO_S16;
+        break;
+    case ENCODING_PCM_FLOAT:
+        spec->format = AUDIO_F32;
+        break;
+    default:
+        return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
+    }
+    spec->channels = resultElements[2];
+    spec->samples = resultElements[3];
+    (*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
+    (*env)->DeleteLocalRef(env, result);
 
     /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
      * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
-
-    if (is16Bit) {
-        jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
-        if (audioBufferLocal) {
-            jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
-            (*env)->DeleteLocalRef(env, audioBufferLocal);
+    switch (audioformat) {
+    case ENCODING_PCM_8BIT:
+        {
+            jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
+            if (audioBufferLocal) {
+                jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
+                (*env)->DeleteLocalRef(env, audioBufferLocal);
+            }
         }
-    }
-    else {
-        jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
-        if (audioBufferLocal) {
-            jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
-            (*env)->DeleteLocalRef(env, audioBufferLocal);
+        break;
+    case ENCODING_PCM_16BIT:
+        {
+            jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
+            if (audioBufferLocal) {
+                jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
+                (*env)->DeleteLocalRef(env, audioBufferLocal);
+            }
         }
+        break;
+    case ENCODING_PCM_FLOAT:
+        {
+            jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
+            if (audioBufferLocal) {
+                jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
+                (*env)->DeleteLocalRef(env, audioBufferLocal);
+            }
+        }
+        break;
+    default:
+        return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
     }
 
     if (jbufobj == NULL) {
-        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
-        return 0;
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer");
+        return SDL_OutOfMemory();
     }
 
     if (iscapture) {
+        captureBufferFormat = audioformat;
         captureBuffer = jbufobj;
     } else {
+        audioBufferFormat = audioformat;
         audioBuffer = jbufobj;
     }
+    numBufferFrames = (*env)->GetArrayLength(env, (jarray)jbufobj);
 
-    isCopy = JNI_FALSE;
+    if (!iscapture) {
+        isCopy = JNI_FALSE;
 
-    if (is16Bit) {
-        if (!iscapture) {
-            audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
-        }
-        audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
-    } else {
-        if (!iscapture) {
+        switch (audioformat) {
+        case ENCODING_PCM_8BIT:
             audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
+            break;
+        case ENCODING_PCM_16BIT:
+            audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
+            break;
+        case ENCODING_PCM_FLOAT:
+            audioBufferPinned = (*env)->GetFloatArrayElements(env, (jfloatArray)audioBuffer, &isCopy);
+            break;
+        default:
+            return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
         }
-        audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
     }
-
-    if (audioBufferStereo) {
-        audioBufferFrames /= 2;
-    }
-
-    return audioBufferFrames;
+    return 0;
 }
 
 int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
@@ -1126,12 +1235,22 @@
 {
     JNIEnv *mAudioEnv = Android_JNI_GetEnv();
 
-    if (audioBuffer16Bit) {
-        (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
-        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
-    } else {
+    switch (audioBufferFormat) {
+    case ENCODING_PCM_8BIT:
         (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
         (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
+        break;
+    case ENCODING_PCM_16BIT:
+        (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
+        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
+        break;
+    case ENCODING_PCM_FLOAT:
+        (*mAudioEnv)->ReleaseFloatArrayElements(mAudioEnv, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT);
+        (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer);
+        break;
+    default:
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled audio buffer format");
+        break;
     }
 
     /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
@@ -1143,16 +1262,8 @@
     jboolean isCopy = JNI_FALSE;
     jint br;
 
-    if (captureBuffer16Bit) {
-        SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
-        br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
-        if (br > 0) {
-            jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
-            br *= 2;
-            SDL_memcpy(buffer, ptr, br);
-            (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
-        }
-    } else {
+    switch (captureBufferFormat) {
+    case ENCODING_PCM_8BIT:
         SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
         br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
         if (br > 0) {
@@ -1160,27 +1271,75 @@
             SDL_memcpy(buffer, ptr, br);
             (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
         }
+        break;
+    case ENCODING_PCM_16BIT:
+        SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / sizeof(Sint16)));
+        br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
+        if (br > 0) {
+            jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
+            br *= sizeof(Sint16);
+            SDL_memcpy(buffer, ptr, br);
+            (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
+        }
+        break;
+    case ENCODING_PCM_FLOAT:
+        SDL_assert((*env)->GetArrayLength(env, (jfloatArray)captureBuffer) == (buflen / sizeof(float)));
+        br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_TRUE);
+        if (br > 0) {
+            jfloat *ptr = (*env)->GetFloatArrayElements(env, (jfloatArray)captureBuffer, &isCopy);
+            br *= sizeof(float);
+            SDL_memcpy(buffer, ptr, br);
+            (*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, (jfloat *)ptr, JNI_ABORT);
+        }
+        break;
+    default:
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled capture buffer format");
+        break;
     }
-
-    return (int) br;
+    return br;
 }
 
 void Android_JNI_FlushCapturedAudio(void)
 {
     JNIEnv *env = Android_JNI_GetEnv();
 #if 0  /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
-    if (captureBuffer16Bit) {
-        const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
-        while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
-    } else {
-        const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
-        while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
+    switch (captureBufferFormat) {
+    case ENCODING_PCM_8BIT:
+        {
+            const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
+            while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
+        }
+        break;
+    case ENCODING_PCM_16BIT:
+        {
+            const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
+            while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
+        }
+        break;
+    case ENCODING_PCM_FLOAT:
+        {
+            const jint len = (*env)->GetArrayLength(env, (jfloatArray)captureBuffer);
+            while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
+        }
+        break;
+    default:
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
+        break;
     }
 #else
-    if (captureBuffer16Bit) {
-        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
-    } else {
+    switch (captureBufferFormat) {
+    case ENCODING_PCM_8BIT:
         (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
+        break;
+    case ENCODING_PCM_16BIT:
+        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
+        break;
+    case ENCODING_PCM_FLOAT:
+        (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE);
+        break;
+    default:
+        __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
+        break;
     }
 #endif
 }
@@ -1846,12 +2005,17 @@
     (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
 }
 
-void Android_JNI_HapticRun(int device_id, int length)
+void Android_JNI_HapticRun(int device_id, float intensity, int length)
 {
     JNIEnv *env = Android_JNI_GetEnv();
-    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, length);
+    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, intensity, length);
 }
 
+void Android_JNI_HapticStop(int device_id)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticStop, device_id);
+}
 
 /* See SDLActivity.java for constants. */
 #define COMMAND_SET_KEEP_SCREEN_ON    5
@@ -2006,10 +2170,34 @@
     return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
 }
 
+SDL_bool SDL_IsAndroidTablet(void)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsTablet);
+}
+
 SDL_bool SDL_IsAndroidTV(void)
 {
     JNIEnv *env = Android_JNI_GetEnv();
     return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV);
+}
+
+SDL_bool SDL_IsChromebook(void)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook);
+}
+
+SDL_bool SDL_IsDeXMode(void)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode);
+}
+
+void SDL_AndroidBackButton(void)
+{
+    JNIEnv *env = Android_JNI_GetEnv();
+    return (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton);
 }
 
 const char * SDL_AndroidGetInternalStoragePath(void)
@@ -2166,6 +2354,48 @@
     }
 }
 
+int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y)
+{
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    int custom_cursor = 0;
+    jintArray pixels;
+    pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h);
+    if (pixels) {
+        (*mEnv)->SetIntArrayRegion(mEnv, pixels, 0, surface->w * surface->h, (int *)surface->pixels);
+        custom_cursor = (*mEnv)->CallStaticIntMethod(mEnv, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y);
+        (*mEnv)->DeleteLocalRef(mEnv, pixels);
+    } else {
+        SDL_OutOfMemory();
+    }
+    return custom_cursor;
+}
+
+
+SDL_bool Android_JNI_SetCustomCursor(int cursorID)
+{
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
+}
+
+SDL_bool Android_JNI_SetSystemCursor(int cursorID)
+{
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
+}
+
+SDL_bool Android_JNI_SupportsRelativeMouse()
+{
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSupportsRelativeMouse);
+}
+
+SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled)
+{
+    JNIEnv *mEnv = Android_JNI_GetEnv();
+    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1));
+}
+
+
 #endif /* __ANDROID__ */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/core/android/SDL_android.h b/source/src/core/android/SDL_android.h
index c800dc6..b2ff32e 100644
--- a/source/src/core/android/SDL_android.h
+++ b/source/src/core/android/SDL_android.h
@@ -19,6 +19,7 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 #include "../../SDL_internal.h"
+#include "SDL_system.h"
 
 /* Set up for C function definitions, even when using C++ */
 #ifdef __cplusplus
@@ -30,6 +31,7 @@
 #include <EGL/eglplatform.h>
 #include <android/native_window_jni.h>
 
+#include "SDL_audio.h"
 #include "SDL_rect.h"
 
 /* Interface from the SDL library into the Android Java activity */
@@ -46,12 +48,16 @@
 extern int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi);
 
 /* Audio support */
-extern int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames);
+extern int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec);
 extern void* Android_JNI_GetAudioBuffer(void);
 extern void Android_JNI_WriteAudioBuffer(void);
 extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
 extern void Android_JNI_FlushCapturedAudio(void);
 extern void Android_JNI_CloseAudioDevice(const int iscapture);
+
+/* Detecting device type */
+extern SDL_bool Android_IsDeXMode();
+extern SDL_bool Android_IsChromebook();
 
 #include "SDL_rwops.h"
 
@@ -78,14 +84,16 @@
 
 /* Haptic support */
 void Android_JNI_PollHapticDevices(void);
-void Android_JNI_HapticRun(int device_id, int length);
+void Android_JNI_HapticRun(int device_id, float intensity, int length);
+void Android_JNI_HapticStop(int device_id);
 
 /* Video */
 void Android_JNI_SuspendScreenSaver(SDL_bool suspend);
 
 /* Touch support */
-int Android_JNI_GetTouchDeviceIds(int **ids);
+int Android_JNI_InitTouch(void);
 void Android_JNI_SetSeparateMouseAndTouch(SDL_bool new_value);
+int Android_JNI_GetTouchDeviceIds(int **ids);
 
 /* Threads */
 #include <jni.h>
@@ -102,6 +110,21 @@
 #include "SDL_messagebox.h"
 int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
 
+/* Cursor support */
+int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
+SDL_bool Android_JNI_SetCustomCursor(int cursorID);
+SDL_bool Android_JNI_SetSystemCursor(int cursorID);
+
+/* Relative mouse support */
+SDL_bool Android_JNI_SupportsRelativeMouse(void);
+SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled);
+
+
+SDL_bool SDL_IsAndroidTablet(void);
+SDL_bool SDL_IsAndroidTV(void);
+SDL_bool SDL_IsChromebook(void);
+SDL_bool SDL_IsDeXMode(void);
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 /* *INDENT-OFF* */
diff --git a/source/src/core/linux/SDL_dbus.c b/source/src/core/linux/SDL_dbus.c
index ff4f0fe..e0d9972 100644
--- a/source/src/core/linux/SDL_dbus.c
+++ b/source/src/core/linux/SDL_dbus.c
@@ -173,17 +173,29 @@
     if (conn) {
         DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
         if (msg) {
-            int firstarg = va_arg(ap, int);
+            int firstarg;
+            va_list ap_reply;
+            va_copy(ap_reply, ap);  /* copy the arg list so we don't compete with D-Bus for it */
+            firstarg = va_arg(ap, int);
             if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
                 DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
                 if (reply) {
-                    firstarg = va_arg(ap, int);
-                    if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap)) {
+                    /* skip any input args, get to output args. */
+                    while ((firstarg = va_arg(ap_reply, int)) != DBUS_TYPE_INVALID) {
+                        /* we assume D-Bus already validated all this. */
+                        { void *dumpptr = va_arg(ap_reply, void*); (void) dumpptr; }
+                        if (firstarg == DBUS_TYPE_ARRAY) {
+                            { const int dumpint = va_arg(ap_reply, int); (void) dumpint; }
+                        }
+                    }
+                    firstarg = va_arg(ap_reply, int);
+                    if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap_reply)) {
                         retval = SDL_TRUE;
                     }
                     dbus.message_unref(reply);
                 }
             }
+            va_end(ap_reply);
             dbus.message_unref(msg);
         }
     }
diff --git a/source/src/core/linux/SDL_dbus.h b/source/src/core/linux/SDL_dbus.h
index 062543d..aa787f2 100644
--- a/source/src/core/linux/SDL_dbus.h
+++ b/source/src/core/linux/SDL_dbus.h
@@ -39,9 +39,8 @@
     void (*bus_add_match)(DBusConnection *, const char *, DBusError *);
     DBusConnection * (*connection_open_private)(const char *, DBusError *);
     void (*connection_set_exit_on_disconnect)(DBusConnection *, dbus_bool_t);
-    dbus_bool_t (*connection_get_is_connected)(DBusConnection *); 	
-    dbus_bool_t (*connection_add_filter)(DBusConnection *, DBusHandleMessageFunction,
-	    void *, DBusFreeFunction);
+    dbus_bool_t (*connection_get_is_connected)(DBusConnection *);
+    dbus_bool_t (*connection_add_filter)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction);
     dbus_bool_t (*connection_try_register_object_path)(DBusConnection *, const char *,
         const DBusObjectPathVTable *, void *, DBusError *);
     dbus_bool_t (*connection_send)(DBusConnection *, DBusMessage *, dbus_uint32_t *);
@@ -51,7 +50,7 @@
     void (*connection_flush)(DBusConnection *);
     dbus_bool_t (*connection_read_write)(DBusConnection *, int);
     DBusDispatchStatus (*connection_dispatch)(DBusConnection *);
-    dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *); 	
+    dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *); 
     DBusMessage *(*message_new_method_call)(const char *, const char *, const char *, const char *);
     dbus_bool_t (*message_append_args)(DBusMessage *, int, ...);
     dbus_bool_t (*message_append_args_valist)(DBusMessage *, int, va_list);
@@ -61,7 +60,7 @@
     dbus_bool_t (*message_iter_next)(DBusMessageIter *);
     void (*message_iter_get_basic)(DBusMessageIter *, void *);
     int (*message_iter_get_arg_type)(DBusMessageIter *);
-    void (*message_iter_recurse)(DBusMessageIter *, DBusMessageIter *); 	 	
+    void (*message_iter_recurse)(DBusMessageIter *, DBusMessageIter *); 
     void (*message_unref)(DBusMessage *);
     void (*error_init)(DBusError *);
     dbus_bool_t (*error_is_set)(const DBusError *);
diff --git a/source/src/core/linux/SDL_evdev.c b/source/src/core/linux/SDL_evdev.c
index a506926..5443c21 100644
--- a/source/src/core/linux/SDL_evdev.c
+++ b/source/src/core/linux/SDL_evdev.c
@@ -101,6 +101,7 @@
     SDL_EVDEV_keyboard_state *kbd;
 } SDL_EVDEV_PrivateData;
 
+#undef _THIS
 #define _THIS SDL_EVDEV_PrivateData *_this
 static _THIS = NULL;
 
diff --git a/source/src/core/linux/SDL_evdev_kbd.c b/source/src/core/linux/SDL_evdev_kbd.c
index 250e644..00a3a54 100644
--- a/source/src/core/linux/SDL_evdev_kbd.c
+++ b/source/src/core/linux/SDL_evdev_kbd.c
@@ -21,6 +21,7 @@
 #include "../../SDL_internal.h"
 
 #include "SDL_evdev_kbd.h"
+#include "SDL_hints.h"
 
 #ifdef SDL_INPUT_LINUXKD
 
@@ -33,6 +34,8 @@
 #include <linux/keyboard.h>
 #include <linux/vt.h>
 #include <linux/tiocl.h> /* for TIOCL_GETSHIFTSTATE */
+
+#include <signal.h>
 
 #include "../../events/SDL_events_c.h"
 #include "SDL_evdev_kbd_default_accents.h"
@@ -191,6 +194,151 @@
     return 0;
 }
 
+static SDL_EVDEV_keyboard_state * kbd_cleanup_state = NULL;
+static int kbd_cleanup_sigactions_installed = 0;
+static int kbd_cleanup_atexit_installed = 0;
+
+static struct sigaction old_sigaction[NSIG];
+
+static int fatal_signals[] =
+{
+    /* Handlers for SIGTERM and SIGINT are installed in SDL_QuitInit. */
+    SIGHUP,  SIGQUIT, SIGILL,  SIGABRT,
+    SIGFPE,  SIGSEGV, SIGPIPE, SIGBUS,
+    SIGSYS
+};
+
+static void kbd_cleanup(void)
+{
+    SDL_EVDEV_keyboard_state* kbd = kbd_cleanup_state;
+    if (kbd == NULL) {
+        return;
+    }
+    kbd_cleanup_state = NULL;
+
+    fprintf(stderr, "(SDL restoring keyboard) ");
+    ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
+}
+
+void
+SDL_EVDEV_kbd_reraise_signal(int sig)
+{
+    raise(sig);
+}
+
+siginfo_t* SDL_EVDEV_kdb_cleanup_siginfo = NULL;
+void*      SDL_EVDEV_kdb_cleanup_ucontext = NULL;
+
+static void kbd_cleanup_signal_action(int signum, siginfo_t* info, void* ucontext)
+{
+    struct sigaction* old_action_p = &(old_sigaction[signum]);
+    sigset_t sigset;
+
+    /* Restore original signal handler before going any further. */
+    sigaction(signum, old_action_p, NULL);
+
+    /* Unmask current signal. */
+    sigemptyset(&sigset);
+    sigaddset(&sigset, signum);
+    sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+
+    /* Save original signal info and context for archeologists. */
+    SDL_EVDEV_kdb_cleanup_siginfo = info;
+    SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
+
+    /* Restore keyboard. */
+    kbd_cleanup();
+
+    /* Reraise signal. */
+    SDL_EVDEV_kbd_reraise_signal(signum);
+}
+
+static void kbd_unregister_emerg_cleanup()
+{
+    int tabidx, signum;
+
+    kbd_cleanup_state = NULL;
+
+    if (!kbd_cleanup_sigactions_installed) {
+        return;
+    }
+    kbd_cleanup_sigactions_installed = 0;
+
+    for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
+        struct sigaction* old_action_p;
+        struct sigaction cur_action;
+        signum = fatal_signals[tabidx];
+        old_action_p = &(old_sigaction[signum]);
+
+        /* Examine current signal action */
+        if (sigaction(signum, NULL, &cur_action))
+            continue;
+
+        /* Check if action installed and not modifed */
+        if (!(cur_action.sa_flags & SA_SIGINFO)
+                || cur_action.sa_sigaction != &kbd_cleanup_signal_action)
+            continue;
+
+        /* Restore original action */
+        sigaction(signum, old_action_p, NULL);
+    }
+}
+
+static void kbd_cleanup_atexit(void)
+{
+    /* Restore keyboard. */
+    kbd_cleanup();
+
+    /* Try to restore signal handlers in case shared library is being unloaded */
+    kbd_unregister_emerg_cleanup();
+}
+
+static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state * kbd)
+{
+    int tabidx, signum;
+
+    if (kbd_cleanup_state != NULL) {
+        return;
+    }
+    kbd_cleanup_state = kbd;
+
+    if (!kbd_cleanup_atexit_installed) {
+        /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
+         * functions that are called when the shared library is unloaded.
+         * -- man atexit(3)
+         */
+        atexit(kbd_cleanup_atexit);
+        kbd_cleanup_atexit_installed = 1;
+    }
+
+    if (kbd_cleanup_sigactions_installed) {
+        return;
+    }
+    kbd_cleanup_sigactions_installed = 1;
+
+    for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
+        struct sigaction* old_action_p;
+        struct sigaction new_action;
+        signum = fatal_signals[tabidx];   
+        old_action_p = &(old_sigaction[signum]);
+        if (sigaction(signum, NULL, old_action_p))
+            continue;
+
+        /* Skip SIGHUP and SIGPIPE if handler is already installed
+         * - assume the handler will do the cleanup
+         */
+        if ((signum == SIGHUP || signum == SIGPIPE)
+                && (old_action_p->sa_handler != SIG_DFL 
+                    || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL))
+            continue;
+
+        new_action = *old_action_p;
+        new_action.sa_flags |= SA_SIGINFO;
+        new_action.sa_sigaction = &kbd_cleanup_signal_action;
+        sigaction(signum, &new_action, NULL);
+    }
+}
+
 SDL_EVDEV_keyboard_state *
 SDL_EVDEV_kbd_init(void)
 {
@@ -238,10 +386,20 @@
             kbd->key_maps = default_key_maps;
         }
 
-        /* Mute the keyboard so keystrokes only generate evdev events
-         * and do not leak through to the console
-         */
-        ioctl(kbd->console_fd, KDSKBMODE, K_OFF);
+        /* Allow inhibiting keyboard mute with env. variable for debugging etc. */
+        if (getenv("SDL_INPUT_LINUX_KEEP_KBD") == NULL) {
+            /* Mute the keyboard so keystrokes only generate evdev events
+             * and do not leak through to the console
+             */
+            ioctl(kbd->console_fd, KDSKBMODE, K_OFF);
+
+            /* Make sure to restore keyboard if application fails to call
+             * SDL_Quit before exit or fatal signal is raised.
+             */
+            if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, SDL_FALSE)) {
+                kbd_register_emerg_cleanup(kbd);
+            }
+        }
     }
 
 #ifdef DUMP_ACCENTS
@@ -259,6 +417,8 @@
     if (!kbd) {
         return;
     }
+
+    kbd_unregister_emerg_cleanup();
 
     if (kbd->console_fd >= 0) {
         /* Restore the original keyboard mode */
@@ -609,7 +769,10 @@
     shift_final = (kbd->shift_state | kbd->slockstate) ^ kbd->lockstate;
     key_map = kbd->key_maps[shift_final];
     if (!key_map) {
+        /* Unsupported shift state (e.g. ctrl = 4, alt = 8), just reset to the default state */
+        kbd->shift_state = 0;
         kbd->slockstate = 0;
+        kbd->lockstate = 0;
         return;
     }
 
diff --git a/source/src/core/linux/SDL_evdev_kbd.h b/source/src/core/linux/SDL_evdev_kbd.h
index 831ba3a..5e51cdd 100644
--- a/source/src/core/linux/SDL_evdev_kbd.h
+++ b/source/src/core/linux/SDL_evdev_kbd.h
@@ -19,6 +19,9 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_evdev_kbd_h_
+#define SDL_evdev_kbd_h_
+
 struct SDL_EVDEV_keyboard_state;
 typedef struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state;
 
@@ -26,4 +29,6 @@
 extern void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down);
 extern void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state);
 
+#endif /* SDL_evdev_kbd_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/core/linux/SDL_udev.c b/source/src/core/linux/SDL_udev.c
index dfbeb79..751e2ca 100644
--- a/source/src/core/linux/SDL_udev.c
+++ b/source/src/core/linux/SDL_udev.c
@@ -63,7 +63,7 @@
 {
     /* cast funcs to char* first, to please GCC's strict aliasing rules. */
     #define SDL_UDEV_SYM(x) \
-        if (!SDL_UDEV_load_sym(#x, (void **) (char *) & _this->x)) return -1
+        if (!SDL_UDEV_load_sym(#x, (void **) (char *) & _this->syms.x)) return -1
 
     SDL_UDEV_SYM(udev_device_get_action);
     SDL_UDEV_SYM(udev_device_get_devnode);
@@ -100,7 +100,7 @@
 SDL_UDEV_hotplug_update_available(void)
 {
     if (_this->udev_mon != NULL) {
-        const int fd = _this->udev_monitor_get_fd(_this->udev_mon);
+        const int fd = _this->syms.udev_monitor_get_fd(_this->udev_mon);
         if (SDL_IOReady(fd, SDL_FALSE, 0)) {
             return SDL_TRUE;
         }
@@ -130,21 +130,21 @@
          * Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
          */
         
-        _this->udev = _this->udev_new();
+        _this->udev = _this->syms.udev_new();
         if (_this->udev == NULL) {
             SDL_UDEV_Quit();
             return SDL_SetError("udev_new() failed");
         }
 
-        _this->udev_mon = _this->udev_monitor_new_from_netlink(_this->udev, "udev");
+        _this->udev_mon = _this->syms.udev_monitor_new_from_netlink(_this->udev, "udev");
         if (_this->udev_mon == NULL) {
             SDL_UDEV_Quit();
             return SDL_SetError("udev_monitor_new_from_netlink() failed");
         }
         
-        _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
-        _this->udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
-        _this->udev_monitor_enable_receiving(_this->udev_mon);
+        _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
+        _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
+        _this->syms.udev_monitor_enable_receiving(_this->udev_mon);
         
         /* Do an initial scan of existing devices */
         SDL_UDEV_Scan();
@@ -170,11 +170,11 @@
     if (_this->ref_count < 1) {
         
         if (_this->udev_mon != NULL) {
-            _this->udev_monitor_unref(_this->udev_mon);
+            _this->syms.udev_monitor_unref(_this->udev_mon);
             _this->udev_mon = NULL;
         }
         if (_this->udev != NULL) {
-            _this->udev_unref(_this->udev);
+            _this->syms.udev_unref(_this->udev);
             _this->udev = NULL;
         }
         
@@ -202,28 +202,28 @@
         return;
     }
    
-    enumerate = _this->udev_enumerate_new(_this->udev);
+    enumerate = _this->syms.udev_enumerate_new(_this->udev);
     if (enumerate == NULL) {
         SDL_UDEV_Quit();
         SDL_SetError("udev_enumerate_new() failed");
         return;
     }
     
-    _this->udev_enumerate_add_match_subsystem(enumerate, "input");
-    _this->udev_enumerate_add_match_subsystem(enumerate, "sound");
+    _this->syms.udev_enumerate_add_match_subsystem(enumerate, "input");
+    _this->syms.udev_enumerate_add_match_subsystem(enumerate, "sound");
     
-    _this->udev_enumerate_scan_devices(enumerate);
-    devs = _this->udev_enumerate_get_list_entry(enumerate);
-    for (item = devs; item; item = _this->udev_list_entry_get_next(item)) {
-        const char *path = _this->udev_list_entry_get_name(item);
-        struct udev_device *dev = _this->udev_device_new_from_syspath(_this->udev, path);
+    _this->syms.udev_enumerate_scan_devices(enumerate);
+    devs = _this->syms.udev_enumerate_get_list_entry(enumerate);
+    for (item = devs; item; item = _this->syms.udev_list_entry_get_next(item)) {
+        const char *path = _this->syms.udev_list_entry_get_name(item);
+        struct udev_device *dev = _this->syms.udev_device_new_from_syspath(_this->udev, path);
         if (dev != NULL) {
             device_event(SDL_UDEV_DEVICEADDED, dev);
-            _this->udev_device_unref(dev);
+            _this->syms.udev_device_unref(dev);
         }
     }
 
-    _this->udev_enumerate_unref(enumerate);
+    _this->syms.udev_enumerate_unref(enumerate);
 }
 
 
@@ -305,7 +305,7 @@
     unsigned long v;
 
     SDL_memset(bitmask, 0, bitmask_len*sizeof(*bitmask));
-    value = _this->udev_device_get_sysattr_value(pdev, attr);
+    value = _this->syms.udev_device_get_sysattr_value(pdev, attr);
     if (!value) {
         return;
     }
@@ -340,8 +340,8 @@
     /* walk up the parental chain until we find the real input device; the
      * argument is very likely a subdevice of this, like eventN */
     pdev = dev;
-    while (pdev && !_this->udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
-        pdev = _this->udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
+    while (pdev && !_this->syms.udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
+        pdev = _this->syms.udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
     }
     if (!pdev) {
         return 0;
@@ -405,28 +405,28 @@
     const char *path;
     SDL_UDEV_CallbackList *item;
     
-    path = _this->udev_device_get_devnode(dev);
+    path = _this->syms.udev_device_get_devnode(dev);
     if (path == NULL) {
         return;
     }
     
-    subsystem = _this->udev_device_get_subsystem(dev);
+    subsystem = _this->syms.udev_device_get_subsystem(dev);
     if (SDL_strcmp(subsystem, "sound") == 0) {
         devclass = SDL_UDEV_DEVICE_SOUND;
     } else if (SDL_strcmp(subsystem, "input") == 0) {
         /* udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c */
         
-        val = _this->udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
         if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
             devclass |= SDL_UDEV_DEVICE_JOYSTICK;
         }
         
-        val = _this->udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
         if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
             devclass |= SDL_UDEV_DEVICE_MOUSE;
         }
         
-        val = _this->udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
         if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
             devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
         }
@@ -437,14 +437,14 @@
            
            Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
         */
-        val = _this->udev_device_get_property_value(dev, "ID_INPUT_KEY");
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEY");
         if (val != NULL && SDL_strcmp(val, "1") == 0 ) {
             devclass |= SDL_UDEV_DEVICE_KEYBOARD;
         }
 
         if (devclass == 0) {
             /* Fall back to old style input classes */
-            val = _this->udev_device_get_property_value(dev, "ID_CLASS");
+            val = _this->syms.udev_device_get_property_value(dev, "ID_CLASS");
             if (val != NULL) {
                 if (SDL_strcmp(val, "joystick") == 0) {
                     devclass = SDL_UDEV_DEVICE_JOYSTICK;
@@ -481,11 +481,11 @@
     }
 
     while (SDL_UDEV_hotplug_update_available()) {
-        dev = _this->udev_monitor_receive_device(_this->udev_mon);
+        dev = _this->syms.udev_monitor_receive_device(_this->udev_mon);
         if (dev == NULL) {
             break;
         }
-        action = _this->udev_device_get_action(dev);
+        action = _this->syms.udev_device_get_action(dev);
 
         if (SDL_strcmp(action, "add") == 0) {
             /* Wait for the device to finish initialization */
@@ -496,7 +496,7 @@
             device_event(SDL_UDEV_DEVICEREMOVED, dev);
         }
         
-        _this->udev_device_unref(dev);
+        _this->syms.udev_device_unref(dev);
     }
 }
 
@@ -547,6 +547,22 @@
     
 }
 
+const SDL_UDEV_Symbols *
+SDL_UDEV_GetUdevSyms(void)
+{
+    if (SDL_UDEV_Init() < 0) {
+        SDL_SetError("Could not initialize UDEV");
+        return NULL;
+    }
+
+    return &_this->syms;
+}
+
+void
+SDL_UDEV_ReleaseUdevSyms(void)
+{
+    SDL_UDEV_Quit();
+}
 
 #endif /* SDL_USE_LIBUDEV */
 
diff --git a/source/src/core/linux/SDL_udev.h b/source/src/core/linux/SDL_udev.h
index edf5187..8be7434 100644
--- a/source/src/core/linux/SDL_udev.h
+++ b/source/src/core/linux/SDL_udev.h
@@ -64,22 +64,13 @@
     struct SDL_UDEV_CallbackList *next;
 } SDL_UDEV_CallbackList;
 
-typedef struct SDL_UDEV_PrivateData
-{
-    const char *udev_library;
-    void *udev_handle;
-    struct udev *udev;
-    struct udev_monitor *udev_mon;
-    int ref_count;
-    SDL_UDEV_CallbackList *first, *last;
-    
-    /* Function pointers */
+typedef struct SDL_UDEV_Symbols {
     const char *(*udev_device_get_action)(struct udev_device *);
     const char *(*udev_device_get_devnode)(struct udev_device *);
     const char *(*udev_device_get_subsystem)(struct udev_device *);
-	struct udev_device *(*udev_device_get_parent_with_subsystem_devtype)(struct udev_device *udev_device, const char *subsystem, const char *devtype);
+    struct udev_device *(*udev_device_get_parent_with_subsystem_devtype)(struct udev_device *udev_device, const char *subsystem, const char *devtype);
     const char *(*udev_device_get_property_value)(struct udev_device *, const char *);
-	const char *(*udev_device_get_sysattr_value)(struct udev_device *udev_device, const char *sysattr);
+    const char *(*udev_device_get_sysattr_value)(struct udev_device *udev_device, const char *sysattr);
     struct udev_device *(*udev_device_new_from_syspath)(struct udev *, const char *);
     void (*udev_device_unref)(struct udev_device *);
     int (*udev_enumerate_add_match_property)(struct udev_enumerate *, const char *, const char *);
@@ -100,6 +91,19 @@
     void (*udev_unref)(struct udev *);
     struct udev_device * (*udev_device_new_from_devnum)(struct udev *udev, char type, dev_t devnum);
     dev_t (*udev_device_get_devnum) (struct udev_device *udev_device);
+} SDL_UDEV_Symbols;
+
+typedef struct SDL_UDEV_PrivateData
+{
+    const char *udev_library;
+    void *udev_handle;
+    struct udev *udev;
+    struct udev_monitor *udev_mon;
+    int ref_count;
+    SDL_UDEV_CallbackList *first, *last;
+    
+    /* Function pointers */
+    SDL_UDEV_Symbols syms;
 } SDL_UDEV_PrivateData;
 
 extern int SDL_UDEV_Init(void);
@@ -110,8 +114,8 @@
 extern void SDL_UDEV_Scan(void);
 extern int SDL_UDEV_AddCallback(SDL_UDEV_Callback cb);
 extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb);
-
-
+extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void);
+extern void SDL_UDEV_ReleaseUdevSyms(void);
 
 
 #endif /* HAVE_LIBUDEV_H */
diff --git a/source/src/core/windows/SDL_windows.c b/source/src/core/windows/SDL_windows.c
index 6624043..4da7d07 100644
--- a/source/src/core/windows/SDL_windows.c
+++ b/source/src/core/windows/SDL_windows.c
@@ -31,6 +31,9 @@
 #ifndef _WIN32_WINNT_VISTA
 #define _WIN32_WINNT_VISTA  0x0600
 #endif
+#ifndef _WIN32_WINNT_WIN7
+#define _WIN32_WINNT_WIN7   0x0601
+#endif
 
 
 /* Sets an error message based on an HRESULT */
diff --git a/source/src/core/winrt/SDL_winrtapp_xaml.cpp b/source/src/core/winrt/SDL_winrtapp_xaml.cpp
index 9789d03..7e2aac8 100644
--- a/source/src/core/winrt/SDL_winrtapp_xaml.cpp
+++ b/source/src/core/winrt/SDL_winrtapp_xaml.cpp
@@ -44,7 +44,7 @@
 #if WINAPI_FAMILY == WINAPI_FAMILY_APP
 extern "C"
 ISwapChainBackgroundPanelNative * WINRT_GlobalSwapChainBackgroundPanelNative = NULL;
-static Windows::Foundation::EventRegistrationToken	WINRT_XAMLAppEventToken;
+static Windows::Foundation::EventRegistrationToken  WINRT_XAMLAppEventToken;
 #endif
 
 
diff --git a/source/src/cpuinfo/SDL_cpuinfo.c b/source/src/cpuinfo/SDL_cpuinfo.c
index 4e2c0f1..4410358 100644
--- a/source/src/cpuinfo/SDL_cpuinfo.c
+++ b/source/src/cpuinfo/SDL_cpuinfo.c
@@ -22,6 +22,7 @@
 #include "SDL_config.h"
 #else
 #include "../SDL_internal.h"
+#include "SDL_simd.h"
 #endif
 
 #if defined(__WIN32__)
@@ -38,6 +39,7 @@
 /* CPU feature detection for SDL */
 
 #include "SDL_cpuinfo.h"
+#include "SDL_assert.h"
 
 #ifdef HAVE_SYSCONF
 #include <unistd.h>
@@ -76,18 +78,19 @@
 #endif
 #endif
 
-#define CPU_HAS_RDTSC   0x00000001
-#define CPU_HAS_ALTIVEC 0x00000002
-#define CPU_HAS_MMX     0x00000004
-#define CPU_HAS_3DNOW   0x00000008
-#define CPU_HAS_SSE     0x00000010
-#define CPU_HAS_SSE2    0x00000020
-#define CPU_HAS_SSE3    0x00000040
-#define CPU_HAS_SSE41   0x00000100
-#define CPU_HAS_SSE42   0x00000200
-#define CPU_HAS_AVX     0x00000400
-#define CPU_HAS_AVX2    0x00000800
-#define CPU_HAS_NEON    0x00001000
+#define CPU_HAS_RDTSC   (1 << 0)
+#define CPU_HAS_ALTIVEC (1 << 1)
+#define CPU_HAS_MMX     (1 << 2)
+#define CPU_HAS_3DNOW   (1 << 3)
+#define CPU_HAS_SSE     (1 << 4)
+#define CPU_HAS_SSE2    (1 << 5)
+#define CPU_HAS_SSE3    (1 << 6)
+#define CPU_HAS_SSE41   (1 << 7)
+#define CPU_HAS_SSE42   (1 << 8)
+#define CPU_HAS_AVX     (1 << 9)
+#define CPU_HAS_AVX2    (1 << 10)
+#define CPU_HAS_NEON    (1 << 11)
+#define CPU_HAS_AVX512F (1 << 12)
 
 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ && !__OpenBSD__
 /* This is the brute force way of detecting instruction sets...
@@ -246,6 +249,7 @@
 static int CPU_CPUIDFeatures[4];
 static int CPU_CPUIDMaxFunction = 0;
 static SDL_bool CPU_OSSavesYMM = SDL_FALSE;
+static SDL_bool CPU_OSSavesZMM = SDL_FALSE;
 
 static void
 CPU_calcCPUIDFeatures(void)
@@ -266,7 +270,7 @@
 
                 /* Check to make sure we can call xgetbv */
                 if (c & 0x08000000) {
-                    /* Call xgetbv to see if YMM register state is saved */
+                    /* Call xgetbv to see if YMM (etc) register state is saved */
 #if defined(__GNUC__) && (defined(i386) || defined(__x86_64__))
                     __asm__(".byte 0x0f, 0x01, 0xd0" : "=a" (a) : "c" (0) : "%edx");
 #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) && (_MSC_FULL_VER >= 160040219) /* VS2010 SP1 */
@@ -280,6 +284,7 @@
                     }
 #endif
                     CPU_OSSavesYMM = ((a & 6) == 6) ? SDL_TRUE : SDL_FALSE;
+                    CPU_OSSavesZMM = (CPU_OSSavesYMM && ((a & 0xe0) == 0xe0)) ? SDL_TRUE : SDL_FALSE;
                 }
             }
         }
@@ -396,6 +401,18 @@
         (void) a; (void) b; (void) c; (void) d;  /* compiler warnings... */
         cpuid(7, a, b, c, d);
         return (b & 0x00000020);
+    }
+    return 0;
+}
+
+static int
+CPU_haveAVX512F(void)
+{
+    if (CPU_OSSavesZMM && (CPU_CPUIDMaxFunction >= 7)) {
+        int a, b, c, d;
+        (void) a; (void) b; (void) c; (void) d;  /* compiler warnings... */
+        cpuid(7, a, b, c, d);
+        return (b & 0x00010000);
     }
     return 0;
 }
@@ -571,6 +588,7 @@
 }
 
 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
+static Uint32 SDL_SIMDAlignment = 0xFFFFFFFF;
 
 static Uint32
 SDL_GetCPUFeatures(void)
@@ -578,41 +596,57 @@
     if (SDL_CPUFeatures == 0xFFFFFFFF) {
         CPU_calcCPUIDFeatures();
         SDL_CPUFeatures = 0;
+        SDL_SIMDAlignment = 4;  /* a good safe base value */
         if (CPU_haveRDTSC()) {
             SDL_CPUFeatures |= CPU_HAS_RDTSC;
         }
         if (CPU_haveAltiVec()) {
             SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
         if (CPU_haveMMX()) {
             SDL_CPUFeatures |= CPU_HAS_MMX;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 8);
         }
         if (CPU_have3DNow()) {
             SDL_CPUFeatures |= CPU_HAS_3DNOW;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 8);
         }
         if (CPU_haveSSE()) {
             SDL_CPUFeatures |= CPU_HAS_SSE;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
         if (CPU_haveSSE2()) {
             SDL_CPUFeatures |= CPU_HAS_SSE2;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
         if (CPU_haveSSE3()) {
             SDL_CPUFeatures |= CPU_HAS_SSE3;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
         if (CPU_haveSSE41()) {
             SDL_CPUFeatures |= CPU_HAS_SSE41;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
         if (CPU_haveSSE42()) {
             SDL_CPUFeatures |= CPU_HAS_SSE42;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
         if (CPU_haveAVX()) {
             SDL_CPUFeatures |= CPU_HAS_AVX;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 32);
         }
         if (CPU_haveAVX2()) {
             SDL_CPUFeatures |= CPU_HAS_AVX2;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 32);
+        }
+        if (CPU_haveAVX512F()) {
+            SDL_CPUFeatures |= CPU_HAS_AVX512F;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 64);
         }
         if (CPU_haveNEON()) {
             SDL_CPUFeatures |= CPU_HAS_NEON;
+            SDL_SIMDAlignment = SDL_max(SDL_SIMDAlignment, 16);
         }
     }
     return SDL_CPUFeatures;
@@ -686,6 +720,12 @@
 }
 
 SDL_bool
+SDL_HasAVX512F(void)
+{
+    return CPU_FEATURE_AVAILABLE(CPU_HAS_AVX512F);
+}
+
+SDL_bool
 SDL_HasNEON(void)
 {
     return CPU_FEATURE_AVAILABLE(CPU_HAS_NEON);
@@ -745,6 +785,44 @@
 }
 
 
+size_t
+SDL_SIMDGetAlignment(void)
+{
+    if (SDL_SIMDAlignment == 0xFFFFFFFF) {
+        SDL_GetCPUFeatures();  /* make sure this has been calculated */
+    }
+    SDL_assert(SDL_SIMDAlignment != 0);
+    return SDL_SIMDAlignment;
+}
+
+void *
+SDL_SIMDAlloc(const size_t len)
+{
+    const size_t alignment = SDL_SIMDGetAlignment();
+    const size_t padding = alignment - (len % alignment);
+    const size_t padded = (padding != alignment) ? (len + padding) : len;
+    Uint8 *retval = NULL;
+    Uint8 *ptr = (Uint8 *) SDL_malloc(padded + alignment + sizeof (void *));
+    if (ptr) {
+        /* store the actual malloc pointer right before our aligned pointer. */
+        retval = ptr + sizeof (void *);
+        retval += alignment - (((size_t) retval) % alignment);
+        *(((void **) retval) - 1) = ptr;
+    }
+    return retval;
+}
+
+void
+SDL_SIMDFree(void *ptr)
+{
+    if (ptr) {
+        void **realptr = (void **) ptr;
+        realptr--;
+        SDL_free(*(((void **) ptr) - 1));
+    }
+}
+
+
 #ifdef TEST_MAIN
 
 #include <stdio.h>
@@ -767,6 +845,7 @@
     printf("SSE4.2: %d\n", SDL_HasSSE42());
     printf("AVX: %d\n", SDL_HasAVX());
     printf("AVX2: %d\n", SDL_HasAVX2());
+    printf("AVX-512F: %d\n", SDL_HasAVX512F());
     printf("NEON: %d\n", SDL_HasNEON());
     printf("RAM: %d MB\n", SDL_GetSystemRAM());
     return 0;
diff --git a/source/src/cpuinfo/SDL_simd.h b/source/src/cpuinfo/SDL_simd.h
new file mode 100644
index 0000000..e2b28bc
--- /dev/null
+++ b/source/src/cpuinfo/SDL_simd.h
@@ -0,0 +1,88 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL.h"
+#include "../SDL_internal.h"
+
+/**
+ * \brief Report the alignment this system needs for SIMD allocations.
+ *
+ * This will return the minimum number of bytes to which a pointer must be
+ *  aligned to be compatible with SIMD instructions on the current machine.
+ *  For example, if the machine supports SSE only, it will return 16, but if
+ *  it supports AVX-512F, it'll return 64 (etc). This only reports values for
+ *  instruction sets SDL knows about, so if your SDL build doesn't have
+ *  SDL_HasAVX512F(), then it might return 16 for the SSE support it sees and
+ *  not 64 for the AVX-512 instructions that exist but SDL doesn't know about.
+ *  Plan accordingly.
+ */
+extern size_t SDL_SIMDGetAlignment(void);
+
+/**
+ * \brief Allocate memory in a SIMD-friendly way.
+ *
+ * This will allocate a block of memory that is suitable for use with SIMD
+ *  instructions. Specifically, it will be properly aligned and padded for
+ *  the system's supported vector instructions.
+ *
+ * The memory returned will be padded such that it is safe to read or write
+ *  an incomplete vector at the end of the memory block. This can be useful
+ *  so you don't have to drop back to a scalar fallback at the end of your
+ *  SIMD processing loop to deal with the final elements without overflowing
+ *  the allocated buffer.
+ *
+ * You must free this memory with SDL_FreeSIMD(), not free() or SDL_free()
+ *  or delete[], etc.
+ *
+ * Note that SDL will only deal with SIMD instruction sets it is aware of;
+ *  for example, SDL 2.0.8 knows that SSE wants 16-byte vectors
+ *  (SDL_HasSSE()), and AVX2 wants 32 bytes (SDL_HasAVX2()), but doesn't
+ *  know that AVX-512 wants 64. To be clear: if you can't decide to use an
+ *  instruction set with an SDL_Has*() function, don't use that instruction
+ *  set with memory allocated through here.
+ *
+ * SDL_AllocSIMD(0) will return a non-NULL pointer, assuming the system isn't
+ *  out of memory.
+ *
+ *  \param len The length, in bytes, of the block to allocated. The actual
+ *             allocated block might be larger due to padding, etc.
+ * \return Pointer to newly-allocated block, NULL if out of memory.
+ *
+ * \sa SDL_SIMDAlignment
+ * \sa SDL_SIMDFree
+ */
+extern void * SDL_SIMDAlloc(const size_t len);
+
+/**
+ * \brief Deallocate memory obtained from SDL_SIMDAlloc
+ *
+ * It is not valid to use this function on a pointer from anything but
+ *  SDL_SIMDAlloc(). It can't be used on pointers from malloc, realloc,
+ *  SDL_malloc, memalign, new[], etc.
+ *
+ * However, SDL_SIMDFree(NULL) is a legal no-op.
+ *
+ * \sa SDL_SIMDAlloc
+ */
+extern void SDL_SIMDFree(void *ptr);
+
+/* vi: set ts=4 sw=4 expandtab: */
+
diff --git a/source/src/dynapi/SDL_dynapi.c b/source/src/dynapi/SDL_dynapi.c
index b898826..97bc218 100644
--- a/source/src/dynapi/SDL_dynapi.c
+++ b/source/src/dynapi/SDL_dynapi.c
@@ -27,6 +27,7 @@
 #if defined(__OS2__)
 #define INCL_DOS
 #define INCL_DOSERRORS
+#include <os2.h>
 #include <dos.h>
 #endif
 
@@ -167,15 +168,10 @@
 #error Write me.
 #endif
 
-
-
-/* Here's the exported entry point that fills in the jump table. */
-/*  Use specific types when an "int" might suffice to keep this sane. */
-typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize);
-extern DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32);
-
-Sint32
-SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
+/* we make this a static function so we can call the correct one without the
+   system's dynamic linker resolving to the wrong version of this. */
+static Sint32
+initialize_jumptable(Uint32 apiver, void *table, Uint32 tablesize)
 {
     SDL_DYNAPI_jump_table *output_jump_table = (SDL_DYNAPI_jump_table *) table;
 
@@ -199,6 +195,18 @@
     /* Safe to call SDL functions now; jump table is initialized! */
 
     return 0;  /* success! */
+}
+
+
+/* Here's the exported entry point that fills in the jump table. */
+/*  Use specific types when an "int" might suffice to keep this sane. */
+typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize);
+extern DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32);
+
+Sint32
+SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
+{
+    return initialize_jumptable(apiver, table, tablesize);
 }
 
 
@@ -260,7 +268,7 @@
 SDL_InitDynamicAPILocked(void)
 {
     const char *libname = SDL_getenv_REAL("SDL_DYNAMIC_API");
-    SDL_DYNAPI_ENTRYFN entry = SDL_DYNAPI_entry;  /* funcs from here by default. */
+    SDL_DYNAPI_ENTRYFN entry = NULL;  /* funcs from here by default. */
 
     if (libname) {
         entry = (SDL_DYNAPI_ENTRYFN) get_sdlapi_entry(libname, "SDL_DYNAPI_entry");
@@ -268,16 +276,15 @@
             /* !!! FIXME: fail to startup here instead? */
             /* !!! FIXME: definitely warn user. */
             /* Just fill in the function pointers from this library. */
-            entry = SDL_DYNAPI_entry;
         }
     }
 
-    if (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) {
+    if (!entry || (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0)) {
         /* !!! FIXME: fail to startup here instead? */
         /* !!! FIXME: definitely warn user. */
         /* Just fill in the function pointers from this library. */
-        if (entry != SDL_DYNAPI_entry) {
-            if (!SDL_DYNAPI_entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table))) {
+        if (!entry) {
+            if (!initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table))) {
                 /* !!! FIXME: now we're screwed. Should definitely abort now. */
             }
         }
diff --git a/source/src/dynapi/SDL_dynapi_overrides.h b/source/src/dynapi/SDL_dynapi_overrides.h
index 1ec2eaf..7454213 100644
--- a/source/src/dynapi/SDL_dynapi_overrides.h
+++ b/source/src/dynapi/SDL_dynapi_overrides.h
@@ -669,3 +669,35 @@
 #define SDL_WinRTGetDeviceFamily SDL_WinRTGetDeviceFamily_REAL
 #define SDL_log10 SDL_log10_REAL
 #define SDL_log10f SDL_log10f_REAL
+#define SDL_GameControllerMappingForDeviceIndex SDL_GameControllerMappingForDeviceIndex_REAL
+#define SDL_LinuxSetThreadPriority SDL_LinuxSetThreadPriority_REAL
+#define SDL_HasAVX512F SDL_HasAVX512F_REAL
+#define SDL_IsChromebook SDL_IsChromebook_REAL
+#define SDL_IsDeXMode SDL_IsDeXMode_REAL
+#define SDL_AndroidBackButton SDL_AndroidBackButton_REAL
+#define SDL_exp SDL_exp_REAL
+#define SDL_expf SDL_expf_REAL
+#define SDL_wcsdup SDL_wcsdup_REAL
+#define SDL_GameControllerRumble SDL_GameControllerRumble_REAL
+#define SDL_JoystickRumble SDL_JoystickRumble_REAL
+#define SDL_NumSensors SDL_NumSensors_REAL
+#define SDL_SensorGetDeviceName SDL_SensorGetDeviceName_REAL
+#define SDL_SensorGetDeviceType SDL_SensorGetDeviceType_REAL
+#define SDL_SensorGetDeviceNonPortableType SDL_SensorGetDeviceNonPortableType_REAL
+#define SDL_SensorGetDeviceInstanceID SDL_SensorGetDeviceInstanceID_REAL
+#define SDL_SensorOpen SDL_SensorOpen_REAL
+#define SDL_SensorFromInstanceID SDL_SensorFromInstanceID_REAL
+#define SDL_SensorGetName SDL_SensorGetName_REAL
+#define SDL_SensorGetType SDL_SensorGetType_REAL
+#define SDL_SensorGetNonPortableType SDL_SensorGetNonPortableType_REAL
+#define SDL_SensorGetInstanceID SDL_SensorGetInstanceID_REAL
+#define SDL_SensorGetData SDL_SensorGetData_REAL
+#define SDL_SensorClose SDL_SensorClose_REAL
+#define SDL_SensorUpdate SDL_SensorUpdate_REAL
+#define SDL_IsTablet SDL_IsTablet_REAL
+#define SDL_GetDisplayOrientation SDL_GetDisplayOrientation_REAL
+#define SDL_HasColorKey SDL_HasColorKey_REAL
+#define SDL_CreateThreadWithStackSize SDL_CreateThreadWithStackSize_REAL
+#define SDL_JoystickGetDevicePlayerIndex SDL_JoystickGetDevicePlayerIndex_REAL
+#define SDL_JoystickGetPlayerIndex SDL_JoystickGetPlayerIndex_REAL
+#define SDL_GameControllerGetPlayerIndex SDL_GameControllerGetPlayerIndex_REAL
diff --git a/source/src/dynapi/SDL_dynapi_procs.h b/source/src/dynapi/SDL_dynapi_procs.h
index b715d33..0a1f3ae 100644
--- a/source/src/dynapi/SDL_dynapi_procs.h
+++ b/source/src/dynapi/SDL_dynapi_procs.h
@@ -707,3 +707,51 @@
 #endif
 SDL_DYNAPI_PROC(double,SDL_log10,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_log10f,(float a),(a),return)
+SDL_DYNAPI_PROC(char*,SDL_GameControllerMappingForDeviceIndex,(int a),(a),return)
+#ifdef __LINUX__
+SDL_DYNAPI_PROC(int,SDL_LinuxSetThreadPriority,(Sint64 a, int b),(a,b),return)
+#endif
+SDL_DYNAPI_PROC(SDL_bool,SDL_HasAVX512F,(void),(),return)
+#ifdef __ANDROID__
+SDL_DYNAPI_PROC(SDL_bool,SDL_IsChromebook,(void),(),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_IsDeXMode,(void),(),return)
+SDL_DYNAPI_PROC(void,SDL_AndroidBackButton,(void),(),return)
+#endif
+SDL_DYNAPI_PROC(double,SDL_exp,(double a),(a),return)
+SDL_DYNAPI_PROC(float,SDL_expf,(float a),(a),return)
+SDL_DYNAPI_PROC(wchar_t*,SDL_wcsdup,(const wchar_t *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GameControllerRumble,(SDL_GameController *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_JoystickRumble,(SDL_Joystick *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_NumSensors,(void),(),return)
+SDL_DYNAPI_PROC(const char*,SDL_SensorGetDeviceName,(int a),(a),return)
+SDL_DYNAPI_PROC(SDL_SensorType,SDL_SensorGetDeviceType,(int a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_SensorGetDeviceNonPortableType,(int a),(a),return)
+SDL_DYNAPI_PROC(SDL_SensorID,SDL_SensorGetDeviceInstanceID,(int a),(a),return)
+SDL_DYNAPI_PROC(SDL_Sensor*,SDL_SensorOpen,(int a),(a),return)
+SDL_DYNAPI_PROC(SDL_Sensor*,SDL_SensorFromInstanceID,(SDL_SensorID a),(a),return)
+SDL_DYNAPI_PROC(const char*,SDL_SensorGetName,(SDL_Sensor *a),(a),return)
+SDL_DYNAPI_PROC(SDL_SensorType,SDL_SensorGetType,(SDL_Sensor *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_SensorGetNonPortableType,(SDL_Sensor *a),(a),return)
+SDL_DYNAPI_PROC(SDL_SensorID,SDL_SensorGetInstanceID,(SDL_Sensor *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_SensorGetData,(SDL_Sensor *a, float *b, int c),(a,b,c),return)
+SDL_DYNAPI_PROC(void,SDL_SensorClose,(SDL_Sensor *a),(a),)
+SDL_DYNAPI_PROC(void,SDL_SensorUpdate,(void),(),)
+SDL_DYNAPI_PROC(SDL_bool,SDL_IsTablet,(void),(),return)
+SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetDisplayOrientation,(int a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_HasColorKey,(SDL_Surface *a),(a),return)
+
+#ifdef SDL_CreateThreadWithStackSize
+#undef SDL_CreateThreadWithStackSize
+#endif
+
+#if defined(__WIN32__) && !defined(HAVE_LIBC)
+SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSize,(SDL_ThreadFunction a, const char *b, const size_t c, void *d, pfnSDL_CurrentBeginThread e, pfnSDL_CurrentEndThread f),(a,b,c,d,e,f),return)
+#elif defined(__OS2__)
+SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSize,(SDL_ThreadFunction a, const char *b, const size_t c, void *d, pfnSDL_CurrentBeginThread e, pfnSDL_CurrentEndThread f),(a,b,c,d,e,f),return)
+#else
+SDL_DYNAPI_PROC(SDL_Thread*,SDL_CreateThreadWithStackSize,(SDL_ThreadFunction a, const char *b, const size_t c, void *d),(a,b,c,d),return)
+#endif
+
+SDL_DYNAPI_PROC(int,SDL_JoystickGetDevicePlayerIndex,(int a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_JoystickGetPlayerIndex,(SDL_Joystick *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GameControllerGetPlayerIndex,(SDL_GameController *a),(a),return)
diff --git a/source/src/events/SDL_displayevents.c b/source/src/events/SDL_displayevents.c
new file mode 100644
index 0000000..6c696af
--- /dev/null
+++ b/source/src/events/SDL_displayevents.c
@@ -0,0 +1,60 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../SDL_internal.h"
+
+/* Display event handling code for SDL */
+
+#include "SDL_events.h"
+#include "SDL_events_c.h"
+
+
+int
+SDL_SendDisplayEvent(SDL_VideoDisplay *display, Uint8 displayevent, int data1)
+{
+    int posted;
+
+    if (!display) {
+        return 0;
+    }
+    switch (displayevent) {
+    case SDL_DISPLAYEVENT_ORIENTATION:
+        if (data1 == SDL_ORIENTATION_UNKNOWN || data1 == display->orientation) {
+            return 0;
+        }
+        display->orientation = (SDL_DisplayOrientation)data1;
+        break;
+    }
+
+    /* Post the event, if desired */
+    posted = 0;
+    if (SDL_GetEventState(SDL_DISPLAYEVENT) == SDL_ENABLE) {
+        SDL_Event event;
+        event.type = SDL_DISPLAYEVENT;
+        event.display.event = displayevent;
+        event.display.display = SDL_GetIndexOfDisplay(display);
+        event.display.data1 = data1;
+        posted = (SDL_PushEvent(&event) > 0);
+    }
+
+    return (posted);
+}
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/events/SDL_displayevents_c.h b/source/src/events/SDL_displayevents_c.h
new file mode 100644
index 0000000..41def7b
--- /dev/null
+++ b/source/src/events/SDL_displayevents_c.h
@@ -0,0 +1,30 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../SDL_internal.h"
+
+#ifndef SDL_displayevents_c_h_
+#define SDL_displayevents_c_h_
+
+extern int SDL_SendDisplayEvent(SDL_VideoDisplay *display, Uint8 displayevent, int data1);
+
+#endif /* SDL_displayevents_c_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/events/SDL_events.c b/source/src/events/SDL_events.c
index f2e5b62..25e8ac4 100644
--- a/source/src/events/SDL_events.c
+++ b/source/src/events/SDL_events.c
@@ -417,6 +417,10 @@
     SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
     SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
+#if 0 /* Leave these events enabled so apps can respond to items being dragged onto them at startup */
+    SDL_EventState(SDL_DROPFILE, SDL_DISABLE);
+    SDL_EventState(SDL_DROPTEXT, SDL_DISABLE);
+#endif
 
     SDL_AtomicSet(&SDL_EventQ.active, 1);
 
@@ -604,6 +608,10 @@
 void
 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
 {
+    /* !!! FIXME: we need to manually SDL_free() the strings in TEXTINPUT and
+       drag'n'drop events if we're flushing them without passing them to the
+       app, but I don't know if this is the right place to do that. */
+
     /* Don't look after we've quit */
     if (!SDL_AtomicGet(&SDL_EventQ.active)) {
         return;
@@ -648,6 +656,13 @@
     /* Check for joystick state change */
     if ((!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] || SDL_JoystickEventState(SDL_QUERY))) {
         SDL_JoystickUpdate();
+    }
+#endif
+
+#if !SDL_SENSOR_DISABLED
+    /* Check for sensor state change */
+    if (!SDL_disabled_events[SDL_SENSORUPDATE >> 8]) {
+        SDL_SensorUpdate();
     }
 #endif
 
@@ -863,6 +878,8 @@
 Uint8
 SDL_EventState(Uint32 type, int state)
 {
+    const SDL_bool isdnd = ((state == SDL_DISABLE) || (state == SDL_ENABLE)) &&
+                           ((type == SDL_DROPFILE) || (type == SDL_DROPTEXT));
     Uint8 current_state;
     Uint8 hi = ((type >> 8) & 0xff);
     Uint8 lo = (type & 0xff);
@@ -898,6 +915,12 @@
         }
     }
 
+    /* turn off drag'n'drop support if we've disabled the events.
+       This might change some UI details at the OS level. */
+    if (isdnd) {
+        SDL_ToggleDragAndDropSupport();
+    }
+
     return current_state;
 }
 
diff --git a/source/src/events/SDL_events_c.h b/source/src/events/SDL_events_c.h
index b1bd277..d9684b5 100644
--- a/source/src/events/SDL_events_c.h
+++ b/source/src/events/SDL_events_c.h
@@ -18,12 +18,19 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_events_c_h_
+#define SDL_events_c_h_
+
 #include "../SDL_internal.h"
 
 /* Useful functions and variables from SDL_events.c */
 #include "SDL_events.h"
 #include "SDL_thread.h"
+#include "../video/SDL_sysvideo.h"
+
 #include "SDL_clipboardevents_c.h"
+#include "SDL_displayevents_c.h"
 #include "SDL_dropevents_c.h"
 #include "SDL_gesture_c.h"
 #include "SDL_keyboard_c.h"
@@ -46,4 +53,6 @@
 
 extern void SDL_SendPendingQuit(void);
 
+#endif /* SDL_events_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/events/SDL_mouse.c b/source/src/events/SDL_mouse.c
index 4f4e62f..ff23c5e 100644
--- a/source/src/events/SDL_mouse.c
+++ b/source/src/events/SDL_mouse.c
@@ -33,11 +33,37 @@
 
 /* The mouse state */
 static SDL_Mouse SDL_mouse;
-static Uint32 SDL_double_click_time = 500;
-static int SDL_double_click_radius = 1;
 
 static int
 SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y);
+
+static void SDLCALL
+SDL_MouseDoubleClickTimeChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_Mouse *mouse = (SDL_Mouse *)userdata;
+
+    if (hint && *hint) {
+        mouse->double_click_time = SDL_atoi(hint);
+    } else {
+#ifdef __WIN32__
+        mouse->double_click_time = GetDoubleClickTime();
+#else
+        mouse->double_click_time = 500;
+#endif
+    }
+}
+
+static void SDLCALL
+SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_Mouse *mouse = (SDL_Mouse *)userdata;
+
+    if (hint && *hint) {
+        mouse->double_click_radius = SDL_atoi(hint);
+    } else {
+        mouse->double_click_radius = 32;    /* 32 pixels seems about right for touch interfaces */
+    }
+}
 
 static void SDLCALL
 SDL_MouseNormalSpeedScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
@@ -83,6 +109,12 @@
 
     SDL_zerop(mouse);
 
+    SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_TIME,
+                        SDL_MouseDoubleClickTimeChanged, mouse);
+
+    SDL_AddHintCallback(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS,
+                        SDL_MouseDoubleClickRadiusChanged, mouse);
+
     SDL_AddHintCallback(SDL_HINT_MOUSE_NORMAL_SPEED_SCALE,
                         SDL_MouseNormalSpeedScaleChanged, mouse);
 
@@ -112,12 +144,6 @@
 SDL_GetMouse(void)
 {
     return &SDL_mouse;
-}
-
-void
-SDL_SetDoubleClickTime(Uint32 interval)
-{
-    SDL_double_click_time = interval;
 }
 
 SDL_Window *
@@ -454,9 +480,9 @@
             if (state == SDL_PRESSED) {
                 Uint32 now = SDL_GetTicks();
 
-                if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + SDL_double_click_time) ||
-                    SDL_abs(mouse->x - clickstate->last_x) > SDL_double_click_radius ||
-                    SDL_abs(mouse->y - clickstate->last_y) > SDL_double_click_radius) {
+                if (SDL_TICKS_PASSED(now, clickstate->last_timestamp + mouse->double_click_time) ||
+                    SDL_abs(mouse->x - clickstate->last_x) > mouse->double_click_radius ||
+                    SDL_abs(mouse->y - clickstate->last_y) > mouse->double_click_radius) {
                     clickstate->click_count = 0;
                 }
                 clickstate->last_timestamp = now;
@@ -688,6 +714,7 @@
 ShouldUseRelativeModeWarp(SDL_Mouse *mouse)
 {
     if (!mouse->SetRelativeMouseMode) {
+        SDL_assert(mouse->WarpMouse);   /* Need this functionality for relative mode warp implementation */
         return SDL_TRUE;
     }
 
@@ -720,6 +747,9 @@
     } else if (mouse->SetRelativeMouseMode(enabled) < 0) {
         if (enabled) {
             /* Fall back to warp mode if native relative mode failed */
+            if (!mouse->WarpMouse) {
+                return SDL_SetError("No relative mode implementation available");
+            }
             mouse->relative_mode_warp = SDL_TRUE;
         }
     }
diff --git a/source/src/events/SDL_mouse_c.h b/source/src/events/SDL_mouse_c.h
index 28089e0..ad44492 100644
--- a/source/src/events/SDL_mouse_c.h
+++ b/source/src/events/SDL_mouse_c.h
@@ -90,6 +90,8 @@
     float relative_speed_scale;
     float scale_accum_x;
     float scale_accum_y;
+    Uint32 double_click_time;
+    int double_click_radius;
     SDL_bool touch_mouse_events;
 
     /* Data for double-click tracking */
@@ -111,9 +113,6 @@
 
 /* Get the mouse state structure */
 SDL_Mouse *SDL_GetMouse(void);
-
-/* Set the default double-click interval */
-extern void SDL_SetDoubleClickTime(Uint32 interval);
 
 /* Set the default mouse cursor */
 extern void SDL_SetDefaultCursor(SDL_Cursor * cursor);
diff --git a/source/src/events/SDL_windowevents.c b/source/src/events/SDL_windowevents.c
index 610fad5..1670841 100644
--- a/source/src/events/SDL_windowevents.c
+++ b/source/src/events/SDL_windowevents.c
@@ -25,30 +25,16 @@
 #include "SDL_events.h"
 #include "SDL_events_c.h"
 #include "SDL_mouse_c.h"
-#include "../video/SDL_sysvideo.h"
 
 
 static int SDLCALL
-RemovePendingResizedEvents(void * userdata, SDL_Event *event)
+RemovePendingSizeChangedAndResizedEvents(void * userdata, SDL_Event *event)
 {
     SDL_Event *new_event = (SDL_Event *)userdata;
 
     if (event->type == SDL_WINDOWEVENT &&
-        event->window.event == SDL_WINDOWEVENT_RESIZED &&
-        event->window.windowID == new_event->window.windowID) {
-        /* We're about to post a new size event, drop the old one */
-        return 0;
-    }
-    return 1;
-}
-
-static int SDLCALL
-RemovePendingSizeChangedEvents(void * userdata, SDL_Event *event)
-{
-    SDL_Event *new_event = (SDL_Event *)userdata;
-
-    if (event->type == SDL_WINDOWEVENT &&
-        event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED &&
+        (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED ||
+         event->window.event == SDL_WINDOWEVENT_RESIZED) &&
         event->window.windowID == new_event->window.windowID) {
         /* We're about to post a new size event, drop the old one */
         return 0;
@@ -200,11 +186,8 @@
         event.window.windowID = window->id;
 
         /* Fixes queue overflow with resize events that aren't processed */
-        if (windowevent == SDL_WINDOWEVENT_RESIZED) {
-            SDL_FilterEvents(RemovePendingResizedEvents, &event);
-        }
         if (windowevent == SDL_WINDOWEVENT_SIZE_CHANGED) {
-            SDL_FilterEvents(RemovePendingSizeChangedEvents, &event);
+            SDL_FilterEvents(RemovePendingSizeChangedAndResizedEvents, &event);
         }
         if (windowevent == SDL_WINDOWEVENT_MOVED) {
             SDL_FilterEvents(RemovePendingMoveEvents, &event);
diff --git a/source/src/events/scancodes_xfree86.h b/source/src/events/scancodes_xfree86.h
index 7d1f844..6e65507 100644
--- a/source/src/events/scancodes_xfree86.h
+++ b/source/src/events/scancodes_xfree86.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef scancodes_xfree86_h_
+#define scancodes_xfree86_h_
+
 #include "../../include/SDL_scancode.h"
 
 /* XFree86 key code to SDL scancode mapping table
@@ -503,4 +507,6 @@
     /*  80 */   SDL_SCANCODE_F12,
 };
 
+#endif /* scancodes_xfree86_h_ */
+
 /* *INDENT-ON* */
diff --git a/source/src/haptic/SDL_haptic.c b/source/src/haptic/SDL_haptic.c
index 4988c30..f23ba18 100644
--- a/source/src/haptic/SDL_haptic.c
+++ b/source/src/haptic/SDL_haptic.c
@@ -389,9 +389,11 @@
 void
 SDL_HapticQuit(void)
 {
+    while (SDL_haptics) {
+        SDL_HapticClose(SDL_haptics);
+    }
+
     SDL_SYS_HapticQuit();
-    SDL_assert(SDL_haptics == NULL);
-    SDL_haptics = NULL;
 }
 
 /*
@@ -765,6 +767,7 @@
     SDL_zerop(efx);
     if (haptic->supported & SDL_HAPTIC_SINE) {
         efx->type = SDL_HAPTIC_SINE;
+        efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
         efx->periodic.period = 1000;
         efx->periodic.magnitude = 0x4000;
         efx->periodic.length = 5000;
diff --git a/source/src/haptic/SDL_haptic_c.h b/source/src/haptic/SDL_haptic_c.h
index 26d900d..390dc78 100644
--- a/source/src/haptic/SDL_haptic_c.h
+++ b/source/src/haptic/SDL_haptic_c.h
@@ -19,7 +19,12 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_haptic_c_h_
+#define SDL_haptic_c_h_
+
 extern int SDL_HapticInit(void);
 extern void SDL_HapticQuit(void);
 
+#endif /* SDL_haptic_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/haptic/android/SDL_syshaptic.c b/source/src/haptic/android/SDL_syshaptic.c
index 1fb2352..7cb289b 100644
--- a/source/src/haptic/android/SDL_syshaptic.c
+++ b/source/src/haptic/android/SDL_syshaptic.c
@@ -195,6 +195,10 @@
 void
 SDL_SYS_HapticQuit(void)
 {
+/* We don't have any way to scan for joysticks (and their vibrators) at init, so don't wipe the list
+ * of joysticks here in case this is a reinit.
+ */
+#if 0
     SDL_hapticlist_item *item = NULL;
     SDL_hapticlist_item *next = NULL;
 
@@ -206,6 +210,7 @@
     SDL_hapticlist = SDL_hapticlist_tail = NULL;
     numhaptics = 0;
     return;
+#endif
 }
 
 
@@ -230,7 +235,12 @@
 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
                         Uint32 iterations)
 {
-    Android_JNI_HapticRun (((SDL_hapticlist_item *)haptic->hwdata)->device_id, effect->effect.leftright.length);
+    float large = effect->effect.leftright.large_magnitude / 32767.0f;
+    float small = effect->effect.leftright.small_magnitude / 32767.0f;
+
+    float total = (large * 0.6f) + (small * 0.4f);
+
+    Android_JNI_HapticRun (((SDL_hapticlist_item *)haptic->hwdata)->device_id, total, effect->effect.leftright.length);
     return 0;
 }
 
@@ -238,6 +248,7 @@
 int
 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
 {
+    Android_JNI_HapticStop (((SDL_hapticlist_item *)haptic->hwdata)->device_id);
     return 0;
 }
 
diff --git a/source/src/haptic/linux/SDL_syshaptic.c b/source/src/haptic/linux/SDL_syshaptic.c
index 9c11a2f..4e4f8a5 100644
--- a/source/src/haptic/linux/SDL_syshaptic.c
+++ b/source/src/haptic/linux/SDL_syshaptic.c
@@ -181,6 +181,9 @@
         SDL_UDEV_Quit();
         return SDL_SetError("Could not setup haptic <-> udev callback");
     }
+
+    /* Force a scan to build the initial device list */
+    SDL_UDEV_Scan();
 #endif /* SDL_USE_LIBUDEV */
 
     return numhaptics;
@@ -798,7 +801,8 @@
         else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN)
             dest->u.periodic.waveform = FF_SAW_DOWN;
         dest->u.periodic.period = CLAMP(periodic->period);
-        dest->u.periodic.magnitude = periodic->magnitude;
+        /* Linux expects 0-65535, so multiply by 2 */
+        dest->u.periodic.magnitude = CLAMP(periodic->magnitude) * 2;
         dest->u.periodic.offset = periodic->offset;
         /* Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift. */
         dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
@@ -905,9 +909,9 @@
         dest->trigger.button = 0;
         dest->trigger.interval = 0;
 
-        /* Rumble */
-        dest->u.rumble.strong_magnitude = leftright->large_magnitude;
-        dest->u.rumble.weak_magnitude = leftright->small_magnitude;
+        /* Rumble (Linux expects 0-65535, so multiply by 2) */
+        dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
+        dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
 
         break;
 
diff --git a/source/src/haptic/windows/SDL_windowshaptic.c b/source/src/haptic/windows/SDL_windowshaptic.c
index 3d7361d..2e806c9 100644
--- a/source/src/haptic/windows/SDL_windowshaptic.c
+++ b/source/src/haptic/windows/SDL_windowshaptic.c
@@ -157,7 +157,7 @@
 
     /* Grab the first mouse haptic device we find. */
     for (item = SDL_hapticlist; item != NULL; item = item->next) {
-        if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
+        if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER) {
             return index;
         }
         ++index;
@@ -173,14 +173,16 @@
 int
 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
 {
-    const struct joystick_hwdata *hwdata = joystick->hwdata;
+    if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
+        return 0;
+    }
 #if SDL_HAPTIC_XINPUT
-    if (hwdata->bXInputHaptic) {
+    if (joystick->hwdata->bXInputHaptic) {
         return 1;
     }
 #endif
 #if SDL_HAPTIC_DINPUT
-    if (hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
+    if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
         return 1;
     }
 #endif
@@ -193,6 +195,9 @@
 int
 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
 {
+    if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
+        return 0;
+    }
     if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) {
         return 0;  /* one is XInput, one is not; not the same device. */
     } else if (joystick->hwdata->bXInputHaptic) {
@@ -208,6 +213,8 @@
 int
 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
 {
+    SDL_assert(joystick->driver == &SDL_WINDOWS_JoystickDriver);
+
     if (joystick->hwdata->bXInputDevice) {
         return SDL_XINPUT_HapticOpenFromJoystick(haptic, joystick);
     } else {
diff --git a/source/src/hidapi/AUTHORS.txt b/source/src/hidapi/AUTHORS.txt
new file mode 100644
index 0000000..7acafd7
--- /dev/null
+++ b/source/src/hidapi/AUTHORS.txt
@@ -0,0 +1,16 @@
+
+HIDAPI Authors:
+
+Alan Ott <alan@signal11.us>:
+	Original Author and Maintainer
+	Linux, Windows, and Mac implementations
+
+Ludovic Rousseau <rousseau@debian.org>:
+	Formatting for Doxygen documentation
+	Bug fixes
+	Correctness fixes
+
+
+For a comprehensive list of contributions, see the commit list at github:
+	http://github.com/signal11/hidapi/commits/master
+
diff --git a/source/src/hidapi/HACKING.txt b/source/src/hidapi/HACKING.txt
new file mode 100644
index 0000000..761d4b6
--- /dev/null
+++ b/source/src/hidapi/HACKING.txt
@@ -0,0 +1,15 @@
+This file is mostly for the maintainer.
+
+1. Build hidapi.dll
+2. Build hidtest.exe in DEBUG and RELEASE
+3. Commit all
+
+4. Run the Following
+	export VERSION=0.1.0
+	export TAG_NAME=hidapi-$VERSION
+	git tag $TAG_NAME
+	git archive --format zip --prefix $TAG_NAME/ $TAG_NAME >../$TAG_NAME.zip
+5. Test the zip file.
+6. Run the following:
+	git push origin $TAG_NAME
+
diff --git a/source/src/hidapi/LICENSE-bsd.txt b/source/src/hidapi/LICENSE-bsd.txt
new file mode 100644
index 0000000..538cdf9
--- /dev/null
+++ b/source/src/hidapi/LICENSE-bsd.txt
@@ -0,0 +1,26 @@
+Copyright (c) 2010, Alan Ott, Signal 11 Software
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of Signal 11 Software nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/source/src/hidapi/LICENSE-gpl3.txt b/source/src/hidapi/LICENSE-gpl3.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/source/src/hidapi/LICENSE-gpl3.txt
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/source/src/hidapi/LICENSE-orig.txt b/source/src/hidapi/LICENSE-orig.txt
new file mode 100644
index 0000000..e3f3380
--- /dev/null
+++ b/source/src/hidapi/LICENSE-orig.txt
@@ -0,0 +1,9 @@
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Copyright 2009, Alan Ott, Signal 11 Software.
+ All Rights Reserved.
+ 
+ This software may be used by anyone for any reason so
+ long as the copyright notice in the source files
+ remains intact.
diff --git a/source/src/hidapi/LICENSE.txt b/source/src/hidapi/LICENSE.txt
new file mode 100644
index 0000000..e1676d4
--- /dev/null
+++ b/source/src/hidapi/LICENSE.txt
@@ -0,0 +1,13 @@
+HIDAPI can be used under one of three licenses.
+
+1. The GNU General Public License, version 3.0, in LICENSE-gpl3.txt
+2. A BSD-Style License, in LICENSE-bsd.txt.
+3. The more liberal original HIDAPI license. LICENSE-orig.txt
+
+The license chosen is at the discretion of the user of HIDAPI. For example:
+1. An author of GPL software would likely use HIDAPI under the terms of the
+GPL.
+
+2. An author of commercial closed-source software would likely use HIDAPI
+under the terms of the BSD-style license or the original HIDAPI license.
+
diff --git a/source/src/hidapi/Makefile.am b/source/src/hidapi/Makefile.am
new file mode 100644
index 0000000..3382a1f
--- /dev/null
+++ b/source/src/hidapi/Makefile.am
@@ -0,0 +1,85 @@
+
+ACLOCAL_AMFLAGS = -I m4
+
+if OS_FREEBSD
+pkgconfigdir=$(prefix)/libdata/pkgconfig
+else
+pkgconfigdir=$(libdir)/pkgconfig
+endif
+
+if OS_LINUX
+pkgconfig_DATA=pc/hidapi-hidraw.pc pc/hidapi-libusb.pc
+else
+pkgconfig_DATA=pc/hidapi.pc
+endif
+
+SUBDIRS=
+
+if OS_LINUX
+SUBDIRS += linux libusb
+endif
+
+if OS_DARWIN
+SUBDIRS += mac
+endif
+
+if OS_IOS
+SUBDIRS += ios
+endif
+
+if OS_FREEBSD
+SUBDIRS += libusb
+endif
+
+if OS_KFREEBSD
+SUBDIRS += libusb
+endif
+
+if OS_WINDOWS
+SUBDIRS += windows
+endif
+
+SUBDIRS += hidtest
+
+if BUILD_TESTGUI
+SUBDIRS += testgui
+endif
+
+EXTRA_DIST = udev doxygen
+
+dist_doc_DATA = \
+ README.txt \
+ AUTHORS.txt \
+ LICENSE-bsd.txt \
+ LICENSE-gpl3.txt \
+ LICENSE-orig.txt \
+ LICENSE.txt
+
+SCMCLEAN_TARGETS= \
+ aclocal.m4 \
+ config.guess \
+ config.sub \
+ configure \
+ config.h.in \
+ depcomp \
+ install-sh \
+ ltmain.sh \
+ missing \
+ mac/Makefile.in \
+ testgui/Makefile.in \
+ libusb/Makefile.in \
+ Makefile.in \
+ linux/Makefile.in \
+ windows/Makefile.in \
+ m4/libtool.m4 \
+ m4/lt~obsolete.m4 \
+ m4/ltoptions.m4 \
+ m4/ltsugar.m4 \
+ m4/ltversion.m4
+
+SCMCLEAN_DIR_TARGETS = \
+ autom4te.cache
+
+scm-clean: distclean
+	rm -f $(SCMCLEAN_TARGETS)
+	rm -Rf $(SCMCLEAN_DIR_TARGETS)
diff --git a/source/src/hidapi/README.txt b/source/src/hidapi/README.txt
new file mode 100644
index 0000000..f19dae4
--- /dev/null
+++ b/source/src/hidapi/README.txt
@@ -0,0 +1,339 @@
+         HIDAPI library for Windows, Linux, FreeBSD and Mac OS X
+        =========================================================
+
+About
+======
+
+HIDAPI is a multi-platform library which allows an application to interface
+with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and Mac
+OS X.  HIDAPI can be either built as a shared library (.so or .dll) or
+can be embedded directly into a target application by adding a single source
+file (per platform) and a single header.
+
+HIDAPI has four back-ends:
+	* Windows (using hid.dll)
+	* Linux/hidraw (using the Kernel's hidraw driver)
+	* Linux/libusb (using libusb-1.0)
+	* FreeBSD (using libusb-1.0)
+	* Mac (using IOHidManager)
+
+On Linux, either the hidraw or the libusb back-end can be used. There are
+tradeoffs, and the functionality supported is slightly different.
+
+Linux/hidraw (linux/hid.c):
+This back-end uses the hidraw interface in the Linux kernel.  While this
+back-end will support both USB and Bluetooth, it has some limitations on
+kernels prior to 2.6.39, including the inability to send or receive feature
+reports.  In addition, it will only communicate with devices which have
+hidraw nodes associated with them.  Keyboards, mice, and some other devices
+which are blacklisted from having hidraw nodes will not work. Fortunately,
+for nearly all the uses of hidraw, this is not a problem.
+
+Linux/FreeBSD/libusb (libusb/hid.c):
+This back-end uses libusb-1.0 to communicate directly to a USB device. This
+back-end will of course not work with Bluetooth devices.
+
+HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses
+Fox Toolkit (http://www.fox-toolkit.org).  It will build on every platform
+which HIDAPI supports.  Since it relies on a 3rd party library, building it
+is optional but recommended because it is so useful when debugging hardware.
+
+What Does the API Look Like?
+=============================
+The API provides the the most commonly used HID functions including sending
+and receiving of input, output, and feature reports.  The sample program,
+which communicates with a heavily hacked up version of the Microchip USB
+Generic HID sample looks like this (with error checking removed for
+simplicity):
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "hidapi.h"
+
+#define MAX_STR 255
+
+int main(int argc, char* argv[])
+{
+	int res;
+	unsigned char buf[65];
+	wchar_t wstr[MAX_STR];
+	hid_device *handle;
+	int i;
+
+	// Initialize the hidapi library
+	res = hid_init();
+
+	// Open the device using the VID, PID,
+	// and optionally the Serial number.
+	handle = hid_open(0x4d8, 0x3f, NULL);
+
+	// Read the Manufacturer String
+	res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
+	wprintf(L"Manufacturer String: %s\n", wstr);
+
+	// Read the Product String
+	res = hid_get_product_string(handle, wstr, MAX_STR);
+	wprintf(L"Product String: %s\n", wstr);
+
+	// Read the Serial Number String
+	res = hid_get_serial_number_string(handle, wstr, MAX_STR);
+	wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr);
+
+	// Read Indexed String 1
+	res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
+	wprintf(L"Indexed String 1: %s\n", wstr);
+
+	// Toggle LED (cmd 0x80). The first byte is the report number (0x0).
+	buf[0] = 0x0;
+	buf[1] = 0x80;
+	res = hid_write(handle, buf, 65);
+
+	// Request state (cmd 0x81). The first byte is the report number (0x0).
+	buf[0] = 0x0;
+	buf[1] = 0x81;
+	res = hid_write(handle, buf, 65);
+
+	// Read requested state
+	res = hid_read(handle, buf, 65);
+
+	// Print out the returned buffer.
+	for (i = 0; i < 4; i++)
+		printf("buf[%d]: %d\n", i, buf[i]);
+
+	// Finalize the hidapi library
+	res = hid_exit();
+
+	return 0;
+}
+
+If you have your own simple test programs which communicate with standard
+hardware development boards (such as those from Microchip, TI, Atmel,
+FreeScale and others), please consider sending me something like the above
+for inclusion into the HIDAPI source.  This will help others who have the
+same hardware as you do.
+
+License
+========
+HIDAPI may be used by one of three licenses as outlined in LICENSE.txt.
+
+Download
+=========
+HIDAPI can be downloaded from github
+	git clone git://github.com/signal11/hidapi.git
+
+Build Instructions
+===================
+
+This section is long. Don't be put off by this. It's not long because it's
+complicated to build HIDAPI; it's quite the opposite.  This section is long
+because of the flexibility of HIDAPI and the large number of ways in which
+it can be built and used.  You will likely pick a single build method.
+
+HIDAPI can be built in several different ways. If you elect to build a
+shared library, you will need to build it from the HIDAPI source
+distribution.  If you choose instead to embed HIDAPI directly into your
+application, you can skip the building and look at the provided platform
+Makefiles for guidance.  These platform Makefiles are located in linux/
+libusb/ mac/ and windows/ and are called Makefile-manual.  In addition,
+Visual Studio projects are provided.  Even if you're going to embed HIDAPI
+into your project, it is still beneficial to build the example programs.
+
+
+Prerequisites:
+---------------
+
+	Linux:
+	-------
+	On Linux, you will need to install development packages for libudev,
+	libusb and optionally Fox-toolkit (for the test GUI). On
+	Debian/Ubuntu systems these can be installed by running:
+	    sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev
+
+	If you downloaded the source directly from the git repository (using
+	git clone), you'll need Autotools:
+	    sudo apt-get install autotools-dev autoconf automake libtool
+
+	FreeBSD:
+	---------
+	On FreeBSD you will need to install GNU make, libiconv, and
+	optionally Fox-Toolkit (for the test GUI). This is done by running
+	the following:
+	    pkg_add -r gmake libiconv fox16
+
+	If you downloaded the source directly from the git repository (using
+	git clone), you'll need Autotools:
+	    pkg_add -r autotools
+
+	Mac:
+	-----
+	On Mac, you will need to install Fox-Toolkit if you wish to build
+	the Test GUI. There are two ways to do this, and each has a slight
+	complication. Which method you use depends on your use case.
+
+	If you wish to build the Test GUI just for your own testing on your
+	own computer, then the easiest method is to install Fox-Toolkit
+	using ports:
+		sudo port install fox
+
+	If you wish to build the TestGUI app bundle to redistribute to
+	others, you will need to install Fox-toolkit from source.  This is
+	because the version of fox that gets installed using ports uses the
+	ports X11 libraries which are not compatible with the Apple X11
+	libraries.  If you install Fox with ports and then try to distribute
+	your built app bundle, it will simply fail to run on other systems.
+	To install Fox-Toolkit manually, download the source package from
+	http://www.fox-toolkit.org, extract it, and run the following from
+	within the extracted source:
+		./configure && make && make install
+
+	Windows:
+	---------
+	On Windows, if you want to build the test GUI, you will need to get
+	the hidapi-externals.zip package from the download site.  This
+	contains pre-built binaries for Fox-toolkit.  Extract
+	hidapi-externals.zip just outside of hidapi, so that
+	hidapi-externals and hidapi are on the same level, as shown:
+
+	     Parent_Folder
+	       |
+	       +hidapi
+	       +hidapi-externals
+
+	Again, this step is not required if you do not wish to build the
+	test GUI.
+
+
+Building HIDAPI into a shared library on Unix Platforms:
+---------------------------------------------------------
+
+On Unix-like systems such as Linux, FreeBSD, Mac, and even Windows, using
+Mingw or Cygwin, the easiest way to build a standard system-installed shared
+library is to use the GNU Autotools build system.  If you checked out the
+source from the git repository, run the following:
+
+	./bootstrap
+	./configure
+	make
+	make install     <----- as root, or using sudo
+
+If you downloaded a source package (ie: if you did not run git clone), you
+can skip the ./bootstrap step.
+
+./configure can take several arguments which control the build. The two most
+likely to be used are:
+	--enable-testgui
+		Enable build of the Test GUI. This requires Fox toolkit to
+		be installed.  Instructions for installing Fox-Toolkit on
+		each platform are in the Prerequisites section above.
+
+	--prefix=/usr
+		Specify where you want the output headers and libraries to
+		be installed. The example above will put the headers in
+		/usr/include and the binaries in /usr/lib. The default is to
+		install into /usr/local which is fine on most systems.
+
+Building the manual way on Unix platforms:
+-------------------------------------------
+
+Manual Makefiles are provided mostly to give the user and idea what it takes
+to build a program which embeds HIDAPI directly inside of it. These should
+really be used as examples only. If you want to build a system-wide shared
+library, use the Autotools method described above.
+
+	To build HIDAPI using the manual makefiles, change to the directory
+	of your platform and run make. For example, on Linux run:
+		cd linux/
+		make -f Makefile-manual
+
+	To build the Test GUI using the manual makefiles:
+		cd testgui/
+		make -f Makefile-manual
+
+Building on Windows:
+---------------------
+
+To build the HIDAPI DLL on Windows using Visual Studio, build the .sln file
+in the windows/ directory.
+
+To build the Test GUI on windows using Visual Studio, build the .sln file in
+the testgui/ directory.
+
+To build HIDAPI using MinGW or Cygwin using Autotools, use the instructions
+in the section titled "Building HIDAPI into a shared library on Unix
+Platforms" above.  Note that building the Test GUI with MinGW or Cygwin will
+require the Windows procedure in the Prerequisites section above (ie:
+hidapi-externals.zip).
+
+To build HIDAPI using MinGW using the Manual Makefiles, see the section
+"Building the manual way on Unix platforms" above.
+
+HIDAPI can also be built using the Windows DDK (now also called the Windows
+Driver Kit or WDK). This method was originally required for the HIDAPI build
+but not anymore. However, some users still prefer this method. It is not as
+well supported anymore but should still work. Patches are welcome if it does
+not. To build using the DDK:
+
+   1. Install the Windows Driver Kit (WDK) from Microsoft.
+   2. From the Start menu, in the Windows Driver Kits folder, select Build
+      Environments, then your operating system, then the x86 Free Build
+      Environment (or one that is appropriate for your system).
+   3. From the console, change directory to the windows/ddk_build/ directory,
+      which is part of the HIDAPI distribution.
+   4. Type build.
+   5. You can find the output files (DLL and LIB) in a subdirectory created
+      by the build system which is appropriate for your environment. On
+      Windows XP, this directory is objfre_wxp_x86/i386.
+
+Cross Compiling
+================
+
+This section talks about cross compiling HIDAPI for Linux using autotools.
+This is useful for using HIDAPI on embedded Linux targets.  These
+instructions assume the most raw kind of embedded Linux build, where all
+prerequisites will need to be built first.  This process will of course vary
+based on your embedded Linux build system if you are using one, such as
+OpenEmbedded or Buildroot.
+
+For the purpose of this section, it will be assumed that the following
+environment variables are exported.
+
+	$ export STAGING=$HOME/out
+	$ export HOST=arm-linux
+
+STAGING and HOST can be modified to suit your setup.
+
+Prerequisites
+--------------
+
+Note that the build of libudev is the very basic configuration.
+
+Build Libusb. From the libusb source directory, run:
+	./configure --host=$HOST --prefix=$STAGING
+	make
+	make install
+
+Build libudev. From the libudev source directory, run:
+	./configure --disable-gudev --disable-introspection --disable-hwdb \
+		 --host=$HOST --prefix=$STAGING
+	make
+	make install
+
+Building HIDAPI
+----------------
+
+Build HIDAPI:
+
+	PKG_CONFIG_DIR= \
+	PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \
+	PKG_CONFIG_SYSROOT_DIR=$STAGING \
+	./configure --host=$HOST --prefix=$STAGING
+
+
+Signal 11 Software - 2010-04-11
+                     2010-07-28
+                     2011-09-10
+                     2012-05-01
+                     2012-07-03
diff --git a/source/src/hidapi/android/hid.cpp b/source/src/hidapi/android/hid.cpp
new file mode 100644
index 0000000..7b8d41c
--- /dev/null
+++ b/source/src/hidapi/android/hid.cpp
@@ -0,0 +1,1159 @@
+//=================== Copyright Valve Corporation, All rights reserved. =======
+//
+// Purpose: A wrapper implementing "HID" API for Android
+//
+//          This layer glues the hidapi API to Android's USB and BLE stack.
+//
+//=============================================================================
+
+#include <jni.h>
+#include <android/log.h>
+#include <pthread.h>
+#include <errno.h>	// For ETIMEDOUT and ECONNRESET
+#include <stdlib.h> // For malloc() and free()
+#include <string.h>	// For memcpy()
+
+#define TAG "hidapi"
+
+// Have error log always available
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+
+#ifdef DEBUG
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
+#else
+#define LOGV(...)
+#define LOGD(...)
+#endif
+
+#define SDL_JAVA_PREFIX                                 org_libsdl_app
+#define CONCAT1(prefix, class, function)                CONCAT2(prefix, class, function)
+#define CONCAT2(prefix, class, function)                Java_ ## prefix ## _ ## class ## _ ## function
+#define HID_DEVICE_MANAGER_JAVA_INTERFACE(function)     CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
+
+#include "../hidapi/hidapi.h"
+
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+
+struct hid_device_
+{
+	int m_nId;
+	int m_nDeviceRefCount;
+};
+
+static JavaVM *g_JVM;
+static pthread_key_t g_ThreadKey;
+
+template<class T>
+class hid_device_ref
+{
+public:
+	hid_device_ref( T *pObject = nullptr ) : m_pObject( nullptr )
+	{
+		SetObject( pObject );
+	}
+
+	hid_device_ref( const hid_device_ref &rhs ) : m_pObject( nullptr )
+	{
+		SetObject( rhs.GetObject() );
+	}
+
+	~hid_device_ref()
+	{
+		SetObject( nullptr );
+	}
+
+	void SetObject( T *pObject )
+	{
+		if ( m_pObject && m_pObject->DecrementRefCount() == 0 )
+		{
+			delete m_pObject;
+		}
+
+		m_pObject = pObject;
+
+		if ( m_pObject )
+		{
+			m_pObject->IncrementRefCount();
+		}
+	}
+
+	hid_device_ref &operator =( T *pObject )
+	{
+		SetObject( pObject );
+		return *this;
+	}
+
+	hid_device_ref &operator =( const hid_device_ref &rhs )
+	{
+		SetObject( rhs.GetObject() );
+		return *this;
+	}
+
+	T *GetObject() const
+	{
+		return m_pObject;
+	}
+
+	T* operator->() const
+	{
+		return m_pObject;
+	}
+
+	operator bool() const
+	{
+		return ( m_pObject != nullptr );
+	}
+
+private:
+	T *m_pObject;
+};
+
+class hid_mutex_guard
+{
+public:
+	hid_mutex_guard( pthread_mutex_t *pMutex ) : m_pMutex( pMutex )
+	{
+		pthread_mutex_lock( m_pMutex );
+	}
+	~hid_mutex_guard()
+	{
+		pthread_mutex_unlock( m_pMutex );
+	}
+
+private:
+	pthread_mutex_t *m_pMutex;
+};
+
+class hid_buffer
+{
+public:
+	hid_buffer() : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
+	{
+	}
+
+	hid_buffer( const uint8_t *pData, size_t nSize ) : m_pData( nullptr ), m_nSize( 0 ), m_nAllocated( 0 )
+	{
+		assign( pData, nSize );
+	}
+
+	~hid_buffer()
+	{
+		delete[] m_pData;
+	}
+
+	void assign( const uint8_t *pData, size_t nSize )
+	{
+		if ( nSize > m_nAllocated )
+		{
+			delete[] m_pData;
+			m_pData = new uint8_t[ nSize ];
+			m_nAllocated = nSize;
+		}
+
+		m_nSize = nSize;
+		memcpy( m_pData, pData, nSize );
+	}
+
+	void clear()
+	{
+		m_nSize = 0;
+	}
+
+	size_t size() const
+	{
+		return m_nSize;
+	}
+
+	const uint8_t *data() const
+	{
+		return m_pData;
+	}
+
+private:
+	uint8_t *m_pData;
+	size_t m_nSize;
+	size_t m_nAllocated;
+};
+
+class hid_buffer_pool
+{
+public:
+	hid_buffer_pool() : m_nSize( 0 ), m_pHead( nullptr ), m_pTail( nullptr ), m_pFree( nullptr )
+	{
+	}
+
+	~hid_buffer_pool()
+	{
+		clear();
+
+		while ( m_pFree )
+		{
+			hid_buffer_entry *pEntry = m_pFree;
+			m_pFree = m_pFree->m_pNext;
+			delete pEntry;
+		}
+	}
+
+	size_t size() const { return m_nSize; }
+
+	const hid_buffer &front() const { return m_pHead->m_buffer; }
+
+	void pop_front()
+	{
+		hid_buffer_entry *pEntry = m_pHead;
+		if ( pEntry )
+		{
+			m_pHead = pEntry->m_pNext;
+			if ( !m_pHead )
+			{
+				m_pTail = nullptr;
+			}
+			pEntry->m_pNext = m_pFree;
+			m_pFree = pEntry;
+			--m_nSize;
+		}
+	}
+
+	void emplace_back( const uint8_t *pData, size_t nSize )
+	{
+		hid_buffer_entry *pEntry;
+
+		if ( m_pFree )
+		{
+			pEntry = m_pFree;
+			m_pFree = m_pFree->m_pNext;
+		}
+		else
+		{
+			pEntry = new hid_buffer_entry;
+		}
+		pEntry->m_pNext = nullptr;
+
+		if ( m_pTail )
+		{
+			m_pTail->m_pNext = pEntry;
+		}
+		else
+		{
+			m_pHead = pEntry;
+		}
+		m_pTail = pEntry;
+
+		pEntry->m_buffer.assign( pData, nSize );
+		++m_nSize;
+	}
+
+	void clear()
+	{
+		while ( size() > 0 )
+		{
+			pop_front();
+		}
+	}
+
+private:
+	struct hid_buffer_entry
+	{
+		hid_buffer m_buffer;
+		hid_buffer_entry *m_pNext;
+	};
+
+	size_t m_nSize;
+	hid_buffer_entry *m_pHead;
+	hid_buffer_entry *m_pTail;
+	hid_buffer_entry *m_pFree;
+};
+
+static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
+{
+	jbyteArray array = env->NewByteArray( nDataLen );
+	jbyte *pBuf = env->GetByteArrayElements( array, NULL );
+	memcpy( pBuf, pData, nDataLen );
+	env->ReleaseByteArrayElements( array, pBuf, 0 );
+
+	return array;
+}
+
+static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
+{
+	size_t nLength = env->GetStringUTFLength( sString );
+	const char *pjChars = env->GetStringUTFChars( sString, NULL );
+	char *psString = (char*)malloc( nLength + 1 );
+	memcpy( psString, pjChars, nLength );
+	psString[ nLength ] = '\0';
+	env->ReleaseStringUTFChars( sString, pjChars );
+	return psString;
+}
+
+static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
+{
+	size_t nLength = env->GetStringLength( sString );
+	const jchar *pjChars = env->GetStringChars( sString, NULL );
+	wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
+	wchar_t *pwChars = pwString;
+	for ( size_t iIndex = 0; iIndex < nLength; ++iIndex )
+	{
+		pwChars[ iIndex ] = pjChars[ iIndex ];
+	}
+	pwString[ nLength ] = '\0';
+	env->ReleaseStringChars( sString, pjChars );
+	return pwString;
+}
+
+static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
+{
+	size_t nLength = wcslen( pwSrc );
+	wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
+	memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
+	pwString[ nLength ] = '\0';
+	return pwString;
+}
+
+static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
+{
+	hid_device_info *pCopy = new hid_device_info;
+	*pCopy = *pInfo;
+	pCopy->path = strdup( pInfo->path );
+	pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
+	pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
+	pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
+	return pCopy;
+}
+
+static void FreeHIDDeviceInfo( hid_device_info *pInfo )
+{
+	free( pInfo->path );
+	free( pInfo->serial_number );
+	free( pInfo->manufacturer_string );
+	free( pInfo->product_string );
+	delete pInfo;
+}
+
+static jclass  g_HIDDeviceManagerCallbackClass;
+static jobject g_HIDDeviceManagerCallbackHandler;
+static jmethodID g_midHIDDeviceManagerOpen;
+static jmethodID g_midHIDDeviceManagerSendOutputReport;
+static jmethodID g_midHIDDeviceManagerSendFeatureReport;
+static jmethodID g_midHIDDeviceManagerGetFeatureReport;
+static jmethodID g_midHIDDeviceManagerClose;
+
+static uint64_t get_timespec_ms( const struct timespec &ts )
+{
+	return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+}
+
+class CHIDDevice
+{
+public:
+	CHIDDevice( int nDeviceID, hid_device_info *pInfo )
+	{
+		m_nId = nDeviceID;
+		m_pInfo = pInfo;
+
+		// The Bluetooth Steam Controller needs special handling
+		const int VALVE_USB_VID	= 0x28DE;
+		const int D0G_BLE2_PID = 0x1106;
+		if ( pInfo->vendor_id == VALVE_USB_VID && pInfo->product_id == D0G_BLE2_PID )
+		{
+			m_bIsBLESteamController = true;
+		}
+	}
+
+	~CHIDDevice()
+	{
+		FreeHIDDeviceInfo( m_pInfo );
+
+		// Note that we don't delete m_pDevice, as the app may still have a reference to it
+	}
+
+	int IncrementRefCount()
+	{
+		int nValue;
+		pthread_mutex_lock( &m_refCountLock );
+		nValue = ++m_nRefCount;
+		pthread_mutex_unlock( &m_refCountLock );
+		return nValue;
+	}
+
+	int DecrementRefCount()
+	{
+		int nValue;
+		pthread_mutex_lock( &m_refCountLock );
+		nValue = --m_nRefCount;
+		pthread_mutex_unlock( &m_refCountLock );
+		return nValue;
+	}
+
+	int GetId()
+	{
+		return m_nId;
+	}
+
+	const hid_device_info *GetDeviceInfo()
+	{
+		return m_pInfo;
+	}
+
+	hid_device *GetDevice()
+	{
+		return m_pDevice;
+	}
+
+	void ExceptionCheck( JNIEnv *env, const char *pszMethodName )
+	{
+		if ( env->ExceptionCheck() )
+		{
+			// Get our exception
+			jthrowable jExcept = env->ExceptionOccurred();
+
+			// Clear the exception so we can call JNI again
+			env->ExceptionClear();
+
+			// Get our exception message
+			jclass jExceptClass = env->GetObjectClass( jExcept );
+			jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
+			jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
+			const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
+
+			// ...and log it.
+			LOGE( "CHIDDevice::%s threw an exception: %s", pszMethodName, pszMessage );
+
+			// Cleanup
+			env->ReleaseStringUTFChars( jMessage, pszMessage );
+			env->DeleteLocalRef( jMessage );
+			env->DeleteLocalRef( jExceptClass );
+			env->DeleteLocalRef( jExcept );
+		}
+	}
+
+	bool BOpen()
+	{
+		// Make sure thread is attached to JVM/env
+		JNIEnv *env;
+		g_JVM->AttachCurrentThread( &env, NULL );
+		pthread_setspecific( g_ThreadKey, (void*)env );
+
+		m_bIsWaitingForOpen = false;
+		m_bOpenResult = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerOpen, m_nId );
+		ExceptionCheck( env, "BOpen" );
+
+		if ( m_bIsWaitingForOpen )
+		{
+			hid_mutex_guard cvl( &m_cvLock );
+
+			const int OPEN_TIMEOUT_SECONDS = 60;
+			struct timespec ts, endtime;
+			clock_gettime( CLOCK_REALTIME, &ts );
+			endtime = ts;
+			endtime.tv_sec += OPEN_TIMEOUT_SECONDS;
+			do
+			{
+				if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
+				{
+					break;
+				}
+			}
+			while ( m_bIsWaitingForOpen && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
+		}
+
+		if ( !m_bOpenResult )
+		{
+			if ( m_bIsWaitingForOpen )
+			{
+				LOGV( "Device open failed - timed out waiting for device permission" );
+			}
+			else
+			{
+				LOGV( "Device open failed" );
+			}
+			return false;
+		}
+
+		m_pDevice = new hid_device;
+		m_pDevice->m_nId = m_nId;
+		m_pDevice->m_nDeviceRefCount = 1;
+		LOGD("Creating device %d (%p), refCount = 1\n", m_pDevice->m_nId, m_pDevice);
+		return true;
+	}
+
+	void SetOpenPending()
+	{
+		m_bIsWaitingForOpen = true;
+	}
+
+	void SetOpenResult( bool bResult )
+	{
+		if ( m_bIsWaitingForOpen )
+		{
+			m_bOpenResult = bResult;
+			m_bIsWaitingForOpen = false;
+			pthread_cond_signal( &m_cv );
+		}
+	}
+
+	void ProcessInput( const uint8_t *pBuf, size_t nBufSize )
+	{
+		hid_mutex_guard l( &m_dataLock );
+
+		size_t MAX_REPORT_QUEUE_SIZE = 16;
+		if ( m_vecData.size() >= MAX_REPORT_QUEUE_SIZE )
+		{
+			m_vecData.pop_front();
+		}
+		m_vecData.emplace_back( pBuf, nBufSize );
+	}
+
+	int GetInput( unsigned char *data, size_t length )
+	{
+		hid_mutex_guard l( &m_dataLock );
+
+		if ( m_vecData.size() == 0 )
+		{
+//			LOGV( "hid_read_timeout no data available" );
+			return 0;
+		}
+
+		const hid_buffer &buffer = m_vecData.front();
+		size_t nDataLen = buffer.size() > length ? length : buffer.size();
+		if ( m_bIsBLESteamController )
+		{
+			data[0] = 0x03;
+			memcpy( data + 1, buffer.data(), nDataLen );
+			++nDataLen;
+		}
+		else
+		{
+			memcpy( data, buffer.data(), nDataLen );
+		}
+		m_vecData.pop_front();
+
+//		LOGV("Read %u bytes", nDataLen);
+//		LOGV("%02x %02x %02x %02x %02x %02x %02x %02x ....",
+//			 data[0], data[1], data[2], data[3],
+//			 data[4], data[5], data[6], data[7]);
+
+		return nDataLen;
+	}
+
+	int SendOutputReport( const unsigned char *pData, size_t nDataLen )
+	{
+		// Make sure thread is attached to JVM/env
+		JNIEnv *env;
+		g_JVM->AttachCurrentThread( &env, NULL );
+		pthread_setspecific( g_ThreadKey, (void*)env );
+
+		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
+		int nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendOutputReport, m_nId, pBuf );
+		ExceptionCheck( env, "SendOutputReport" );
+
+		env->DeleteLocalRef( pBuf );
+		return nRet;
+	}
+
+	int SendFeatureReport( const unsigned char *pData, size_t nDataLen )
+	{
+		// Make sure thread is attached to JVM/env
+		JNIEnv *env;
+		g_JVM->AttachCurrentThread( &env, NULL );
+		pthread_setspecific( g_ThreadKey, (void*)env );
+
+		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
+		int nRet = env->CallIntMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerSendFeatureReport, m_nId, pBuf );
+		ExceptionCheck( env, "SendFeatureReport" );
+		env->DeleteLocalRef( pBuf );
+		return nRet;
+	}
+
+	void ProcessFeatureReport( const uint8_t *pBuf, size_t nBufSize )
+	{
+		hid_mutex_guard cvl( &m_cvLock );
+		if ( m_bIsWaitingForFeatureReport )
+		{
+			m_featureReport.assign( pBuf, nBufSize );
+
+			m_bIsWaitingForFeatureReport = false;
+			m_nFeatureReportError = 0;
+			pthread_cond_signal( &m_cv );
+		}
+	}
+
+	int GetFeatureReport( unsigned char *pData, size_t nDataLen )
+	{
+		// Make sure thread is attached to JVM/env
+		JNIEnv *env;
+		g_JVM->AttachCurrentThread( &env, NULL );
+		pthread_setspecific( g_ThreadKey, (void*)env );
+
+		{
+			hid_mutex_guard cvl( &m_cvLock );
+			if ( m_bIsWaitingForFeatureReport )
+			{
+				LOGV( "Get feature report already ongoing... bail" );
+				return -1; // Read already ongoing, we currently do not serialize, TODO
+			}
+			m_bIsWaitingForFeatureReport = true;
+		}
+
+		jbyteArray pBuf = NewByteArray( env, pData, nDataLen );
+		int nRet = env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerGetFeatureReport, m_nId, pBuf ) ? 0 : -1;
+		ExceptionCheck( env, "GetFeatureReport" );
+		env->DeleteLocalRef( pBuf );
+		if ( nRet < 0 )
+		{
+			LOGV( "GetFeatureReport failed" );
+			m_bIsWaitingForFeatureReport = false;
+			return -1;
+		}
+
+		{
+			hid_mutex_guard cvl( &m_cvLock );
+			if ( m_bIsWaitingForFeatureReport )
+			{
+				LOGV("=== Going to sleep" );
+				// Wait in CV until we are no longer waiting for a feature report.
+				const int FEATURE_REPORT_TIMEOUT_SECONDS = 2;
+				struct timespec ts, endtime;
+				clock_gettime( CLOCK_REALTIME, &ts );
+				endtime = ts;
+				endtime.tv_sec += FEATURE_REPORT_TIMEOUT_SECONDS;
+				do
+				{
+					if ( pthread_cond_timedwait( &m_cv, &m_cvLock, &endtime ) != 0 )
+					{
+						break;
+					}
+				}
+				while ( m_bIsWaitingForFeatureReport && get_timespec_ms( ts ) < get_timespec_ms( endtime ) );
+
+				// We are back
+				if ( m_bIsWaitingForFeatureReport )
+				{
+					m_nFeatureReportError = -ETIMEDOUT;
+					m_bIsWaitingForFeatureReport = false;
+				}
+				LOGV( "=== Got feature report err=%d", m_nFeatureReportError );
+				if ( m_nFeatureReportError != 0 )
+				{
+					return m_nFeatureReportError;
+				}
+			}
+
+			size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
+			memcpy( pData, m_featureReport.data(), uBytesToCopy );
+			m_featureReport.clear();
+			LOGV( "=== Got %u bytes", uBytesToCopy );
+
+			return uBytesToCopy;
+		}
+	}
+
+	void Close( bool bDeleteDevice )
+	{
+		// Make sure thread is attached to JVM/env
+		JNIEnv *env;
+		g_JVM->AttachCurrentThread( &env, NULL );
+		pthread_setspecific( g_ThreadKey, (void*)env );
+
+		env->CallVoidMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerClose, m_nId );
+		ExceptionCheck( env, "Close" );
+	
+		hid_mutex_guard dataLock( &m_dataLock );
+		m_vecData.clear();
+
+		// Clean and release pending feature report reads
+		hid_mutex_guard cvLock( &m_cvLock );
+		m_featureReport.clear();
+		m_bIsWaitingForFeatureReport = false;
+		m_nFeatureReportError = -ECONNRESET;
+		pthread_cond_broadcast( &m_cv );
+
+		if ( bDeleteDevice )
+		{
+			delete m_pDevice;
+			m_pDevice = nullptr;
+		}
+	}
+
+private:
+	pthread_mutex_t m_refCountLock = PTHREAD_MUTEX_INITIALIZER;
+	int m_nRefCount = 0;
+	int m_nId = 0;
+	hid_device_info *m_pInfo = nullptr;
+	hid_device *m_pDevice = nullptr;
+	bool m_bIsBLESteamController = false;
+
+	pthread_mutex_t m_dataLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access m_vecData
+	hid_buffer_pool m_vecData;
+
+	// For handling get_feature_report
+	pthread_mutex_t m_cvLock = PTHREAD_MUTEX_INITIALIZER; // This lock has to be held to access any variables below
+	pthread_cond_t m_cv = PTHREAD_COND_INITIALIZER;
+	bool m_bIsWaitingForOpen = false;
+	bool m_bOpenResult = false;
+	bool m_bIsWaitingForFeatureReport = false;
+	int m_nFeatureReportError = 0;
+	hid_buffer m_featureReport;
+
+public:
+	hid_device_ref<CHIDDevice> next;
+};
+
+class CHIDDevice;
+static pthread_mutex_t g_DevicesMutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t g_DevicesRefCountMutex = PTHREAD_MUTEX_INITIALIZER;
+static hid_device_ref<CHIDDevice> g_Devices;
+
+static hid_device_ref<CHIDDevice> FindDevice( int nDeviceId )
+{
+	hid_device_ref<CHIDDevice> pDevice;
+
+	hid_mutex_guard l( &g_DevicesMutex );
+	for ( pDevice = g_Devices; pDevice; pDevice = pDevice->next )
+	{
+		if ( pDevice->GetId() == nDeviceId )
+		{
+			break;
+		}
+	}
+	return pDevice;
+}
+
+static void ThreadDestroyed(void* value)
+{
+	/* The thread is being destroyed, detach it from the Java VM and set the g_ThreadKey value to NULL as required */
+	JNIEnv *env = (JNIEnv*) value;
+	if (env != NULL) {
+		g_JVM->DetachCurrentThread();
+		pthread_setspecific(g_ThreadKey, NULL);
+	}
+}
+
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface );
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
+
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
+{
+	LOGV( "HIDDeviceRegisterCallback()");
+
+	env->GetJavaVM( &g_JVM );
+
+	/*
+	 * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
+	 * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
+	 */
+	if (pthread_key_create(&g_ThreadKey, ThreadDestroyed) != 0) {
+		__android_log_print(ANDROID_LOG_ERROR, TAG, "Error initializing pthread key");
+	}
+
+	if ( g_HIDDeviceManagerCallbackHandler != NULL )
+	{
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
+		g_HIDDeviceManagerCallbackClass = NULL;
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
+		g_HIDDeviceManagerCallbackHandler = NULL;
+	}
+
+	g_HIDDeviceManagerCallbackHandler = env->NewGlobalRef( thiz );
+	jclass objClass = env->GetObjectClass( thiz );
+	if ( objClass )
+	{
+		g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
+		g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
+		if ( !g_midHIDDeviceManagerOpen )
+		{
+			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing openDevice" );
+		}
+		g_midHIDDeviceManagerSendOutputReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendOutputReport", "(I[B)I" );
+		if ( !g_midHIDDeviceManagerSendOutputReport )
+		{
+			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendOutputReport" );
+		}
+		g_midHIDDeviceManagerSendFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "sendFeatureReport", "(I[B)I" );
+		if ( !g_midHIDDeviceManagerSendFeatureReport )
+		{
+			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing sendFeatureReport" );
+		}
+		g_midHIDDeviceManagerGetFeatureReport = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "getFeatureReport", "(I[B)Z" );
+		if ( !g_midHIDDeviceManagerGetFeatureReport )
+		{
+			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing getFeatureReport" );
+		}
+		g_midHIDDeviceManagerClose = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "closeDevice", "(I)V" );
+		if ( !g_midHIDDeviceManagerClose )
+		{
+			__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing closeDevice" );
+		}
+		env->DeleteLocalRef( objClass );
+	}
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
+{
+	LOGV("HIDDeviceReleaseCallback");
+	if ( env->IsSameObject( thiz, g_HIDDeviceManagerCallbackHandler ) )
+	{
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackClass );
+		g_HIDDeviceManagerCallbackClass = NULL;
+		env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
+		g_HIDDeviceManagerCallbackHandler = NULL;
+	}
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface )
+{
+	LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
+
+	hid_device_info *pInfo = new hid_device_info;
+	memset( pInfo, 0, sizeof( *pInfo ) );
+	pInfo->path = CreateStringFromJString( env, sIdentifier );
+	pInfo->vendor_id = nVendorId;
+	pInfo->product_id = nProductId;
+	pInfo->serial_number = CreateWStringFromJString( env, sSerialNumber );
+	pInfo->release_number = nReleaseNumber;
+	pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer );
+	pInfo->product_string = CreateWStringFromJString( env, sProduct );
+	pInfo->interface_number = nInterface;
+
+	hid_device_ref<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
+
+	hid_mutex_guard l( &g_DevicesMutex );
+	hid_device_ref<CHIDDevice> pLast, pCurr;
+	for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
+	{
+		continue;
+	}
+	if ( pLast )
+	{
+		pLast->next = pDevice;
+	}
+	else
+	{
+		g_Devices = pDevice;
+	}
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
+{
+	LOGV( "HIDDeviceOpenPending() id=%d\n", nDeviceID );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
+	if ( pDevice )
+	{
+		pDevice->SetOpenPending();
+	}
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
+{
+	LOGV( "HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false" );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
+	if ( pDevice )
+	{
+		pDevice->SetOpenResult( bOpened );
+	}
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
+{
+	LOGV( "HIDDeviceDisconnected() id=%d\n", nDeviceID );
+	hid_device_ref<CHIDDevice> pDevice;
+	{
+		hid_mutex_guard l( &g_DevicesMutex );
+		hid_device_ref<CHIDDevice> pLast, pCurr;
+		for ( pCurr = g_Devices; pCurr; pLast = pCurr, pCurr = pCurr->next )
+		{
+			if ( pCurr->GetId() == nDeviceID )
+			{
+				pDevice = pCurr;
+
+				if ( pLast )
+				{
+					pLast->next = pCurr->next;
+				}
+				else
+				{
+					g_Devices = pCurr->next;
+				}
+			}
+		}
+	}
+	if ( pDevice )
+	{
+		pDevice->Close( false );
+	}
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
+{
+	jbyte *pBuf = env->GetByteArrayElements(value, NULL);
+	jsize nBufSize = env->GetArrayLength(value);
+
+//	LOGV( "HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
+	if ( pDevice )
+	{
+		pDevice->ProcessInput( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
+	}
+
+	env->ReleaseByteArrayElements(value, pBuf, 0);
+}
+
+extern "C"
+JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
+{
+	jbyte *pBuf = env->GetByteArrayElements(value, NULL);
+	jsize nBufSize = env->GetArrayLength(value);
+
+	LOGV( "HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( nDeviceID );
+	if ( pDevice )
+	{
+		pDevice->ProcessFeatureReport( reinterpret_cast< const uint8_t* >( pBuf ), nBufSize );
+	}
+
+	env->ReleaseByteArrayElements(value, pBuf, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C"
+{
+
+int hid_init(void)
+{
+	return 0;
+}
+
+struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+	struct hid_device_info *root = NULL;
+	hid_mutex_guard l( &g_DevicesMutex );
+	for ( hid_device_ref<CHIDDevice> pDevice = g_Devices; pDevice; pDevice = pDevice->next )
+	{
+		const hid_device_info *info = pDevice->GetDeviceInfo();
+		if ( ( vendor_id == 0 && product_id == 0 ) ||
+			 ( vendor_id == info->vendor_id && product_id == info->product_id ) )
+		{
+			hid_device_info *dev = CopyHIDDeviceInfo( info );
+			dev->next = root;
+			root = dev;
+		}
+	}
+	return root;
+}
+
+void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
+{
+	while ( devs )
+	{
+		struct hid_device_info *next = devs->next;
+		FreeHIDDeviceInfo( devs );
+		devs = next;
+	}
+}
+
+HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
+{
+	// TODO: Implement
+	return NULL;
+}
+
+HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
+{
+	LOGV( "hid_open_path( %s )", path );
+
+	hid_device_ref< CHIDDevice > pDevice;
+	{
+		hid_mutex_guard r( &g_DevicesRefCountMutex );
+		hid_mutex_guard l( &g_DevicesMutex );
+		for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
+		{
+			if ( strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 ) 
+			{
+				hid_device *pValue = pCurr->GetDevice();
+				if ( pValue )
+				{
+					++pValue->m_nDeviceRefCount;
+					LOGD("Incrementing device %d (%p), refCount = %d\n", pValue->m_nId, pValue, pValue->m_nDeviceRefCount);
+					return pValue;
+				}
+
+				// Hold a shared pointer to the controller for the duration
+				pDevice = pCurr;
+				break;
+			}
+		}
+	}
+	if ( pDevice && pDevice->BOpen() )
+	{
+		return pDevice->GetDevice();
+	}
+	return NULL;
+}
+
+int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
+{
+	LOGV( "hid_write id=%d length=%u", device->m_nId, length );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		return pDevice->SendOutputReport( data, length );
+	}
+	return -1; // Controller was disconnected
+}
+
+// TODO: Implement timeout?
+int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
+{
+//	LOGV( "hid_read_timeout id=%d length=%u timeout=%d", device->m_nId, length, milliseconds );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		return pDevice->GetInput( data, length );
+	}
+	LOGV( "controller was disconnected" );
+	return -1; // Controller was disconnected
+}
+
+// TODO: Implement blocking
+int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
+{
+	LOGV( "hid_read id=%d length=%u", device->m_nId, length );
+	return hid_read_timeout( device, data, length, 0 );
+}
+
+// TODO: Implement?
+int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
+{
+	return -1;
+}
+
+int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
+{
+	LOGV( "hid_send_feature_report id=%d length=%u", device->m_nId, length );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		return pDevice->SendFeatureReport( data, length );
+	}
+	return -1; // Controller was disconnected
+}
+
+
+// Synchronous operation. Will block until completed.
+int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
+{
+	LOGV( "hid_get_feature_report id=%d length=%u", device->m_nId, length );
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		return pDevice->GetFeatureReport( data, length );
+	}
+	return -1; // Controller was disconnected
+}
+
+
+void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
+{
+	LOGV( "hid_close id=%d", device->m_nId );
+	hid_mutex_guard r( &g_DevicesRefCountMutex );
+	LOGD("Decrementing device %d (%p), refCount = %d\n", device->m_nId, device, device->m_nDeviceRefCount - 1);
+	if ( --device->m_nDeviceRefCount == 0 )
+	{
+		hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+		if ( pDevice )
+		{
+			pDevice->Close( true );
+		}
+		else
+		{
+			delete device;
+		}
+		LOGD("Deleted device %p\n", device);
+	}
+
+}
+
+int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
+{
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		wcsncpy( string, pDevice->GetDeviceInfo()->manufacturer_string, maxlen );
+		return 0;
+	}
+	return -1;
+}
+
+int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
+{
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		wcsncpy( string, pDevice->GetDeviceInfo()->product_string, maxlen );
+		return 0;
+	}
+	return -1;
+}
+
+int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
+{
+	hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
+	if ( pDevice )
+	{
+		wcsncpy( string, pDevice->GetDeviceInfo()->serial_number, maxlen );
+		return 0;
+	}
+	return -1;
+}
+
+int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
+{
+	return -1;
+}
+
+HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
+{
+	return NULL;
+}
+
+int hid_exit(void)
+{
+	return 0;
+}
+
+}
diff --git a/source/src/hidapi/android/jni/Android.mk b/source/src/hidapi/android/jni/Android.mk
new file mode 100644
index 0000000..4462e88
--- /dev/null
+++ b/source/src/hidapi/android/jni/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+
+HIDAPI_ROOT_REL:= ../..
+HIDAPI_ROOT_ABS:= $(LOCAL_PATH)/../..
+
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS += -std=c++11
+
+LOCAL_SRC_FILES := \
+  $(HIDAPI_ROOT_REL)/android/hid.cpp
+
+LOCAL_MODULE := libhidapi
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/source/src/hidapi/android/jni/Application.mk b/source/src/hidapi/android/jni/Application.mk
new file mode 100644
index 0000000..4fc6ba5
--- /dev/null
+++ b/source/src/hidapi/android/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_STL := gnustl_static
+APP_ABI := armeabi-v7a
diff --git a/source/src/hidapi/android/project.properties b/source/src/hidapi/android/project.properties
new file mode 100644
index 0000000..6e18427
--- /dev/null
+++ b/source/src/hidapi/android/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-21
diff --git a/source/src/hidapi/bootstrap b/source/src/hidapi/bootstrap
new file mode 100755
index 0000000..81e9b74
--- /dev/null
+++ b/source/src/hidapi/bootstrap
@@ -0,0 +1,2 @@
+#!/bin/sh -x
+autoreconf --install --verbose --force
diff --git a/source/src/hidapi/configure.ac b/source/src/hidapi/configure.ac
new file mode 100644
index 0000000..c6747f9
--- /dev/null
+++ b/source/src/hidapi/configure.ac
@@ -0,0 +1,236 @@
+AC_PREREQ(2.63)
+
+# Version number. This is currently the only place.
+m4_define([HIDAPI_MAJOR],   0)
+m4_define([HIDAPI_MINOR],   8)
+m4_define([HIDAPI_RELEASE], 0)
+m4_define([HIDAPI_RC],      -rc1)
+m4_define([VERSION_STRING], HIDAPI_MAJOR[.]HIDAPI_MINOR[.]HIDAPI_RELEASE[]HIDAPI_RC)
+
+AC_INIT([hidapi],[VERSION_STRING],[alan@signal11.us])
+
+# Library soname version
+# Follow the following rules (particularly the ones in the second link):
+#  http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+#  http://sourceware.org/autobook/autobook/autobook_91.html
+lt_current="0"
+lt_revision="0"
+lt_age="0"
+LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}"
+
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([foreign -Wall -Werror])
+AC_CONFIG_MACRO_DIR([m4])
+
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+LT_INIT
+
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_OBJC
+PKG_PROG_PKG_CONFIG
+
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+hidapi_lib_error() {
+	echo ""
+	echo "  Library $1 was not found on this system."
+	echo "  Please install it and re-run ./configure"
+	echo ""
+	exit 1
+}
+
+hidapi_prog_error() {
+	echo ""
+	echo "  Program $1 was not found on this system."
+	echo "  This program is part of $2."
+	echo "  Please install it and re-run ./configure"
+	echo ""
+	exit 1
+}
+
+AC_MSG_CHECKING([operating system])
+AC_MSG_RESULT($host)
+case $host in
+*-linux*)
+	AC_MSG_RESULT([ (Linux back-end)])
+	AC_DEFINE(OS_LINUX, 1, [Linux implementations])
+	AC_SUBST(OS_LINUX)
+	backend="linux"
+	os="linux"
+	threads="pthreads"
+
+	# HIDAPI/hidraw libs
+	PKG_CHECK_MODULES([libudev], [libudev], true, [hidapi_lib_error libudev])
+	LIBS_HIDRAW_PR+=" $libudev_LIBS"
+	CFLAGS_HIDRAW+=" $libudev_CFLAGS"
+
+	# HIDAPI/libusb libs
+	AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE+=" -lrt"], [hidapi_lib_error librt])
+	PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0])
+	LIBS_LIBUSB_PRIVATE+=" $libusb_LIBS"
+	CFLAGS_LIBUSB+=" $libusb_CFLAGS"
+	;;
+*-darwin*)
+	AC_MSG_RESULT([ (Mac OS X back-end)])
+	AC_DEFINE(OS_DARWIN, 1, [Mac implementation])
+	AC_SUBST(OS_DARWIN)
+	backend="mac"
+	os="darwin"
+	threads="pthreads"
+	LIBS="${LIBS} -framework IOKit -framework CoreFoundation"
+	;;
+*-freebsd*)
+	AC_MSG_RESULT([ (FreeBSD back-end)])
+	AC_DEFINE(OS_FREEBSD, 1, [FreeBSD implementation])
+	AC_SUBST(OS_FREEBSD)
+	backend="libusb"
+	os="freebsd"
+	threads="pthreads"
+
+	CFLAGS="$CFLAGS -I/usr/local/include"
+	LDFLAGS="$LDFLAGS -L/usr/local/lib"
+	LIBS="${LIBS}"
+	AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb])
+	AC_CHECK_LIB([iconv], [iconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv])
+	echo libs_priv: $LIBS_LIBUSB_PRIVATE
+	;;
+*-kfreebsd*)
+	AC_MSG_RESULT([ (kFreeBSD back-end)])
+	AC_DEFINE(OS_KFREEBSD, 1, [kFreeBSD implementation])
+	AC_SUBST(OS_KFREEBSD)
+	backend="libusb"
+	os="kfreebsd"
+	threads="pthreads"
+
+	AC_CHECK_LIB([usb], [libusb_init], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lusb"], [hidapi_lib_error libusb])
+	echo libs_priv: $LIBS_LIBUSB_PRIVATE
+	;;
+*-mingw*)
+	AC_MSG_RESULT([ (Windows back-end, using MinGW)])
+	backend="windows"
+	os="windows"
+	threads="windows"
+	win_implementation="mingw"
+	;;
+*-cygwin*)
+	AC_MSG_RESULT([ (Windows back-end, using Cygwin)])
+	backend="windows"
+	os="windows"
+	threads="windows"
+	win_implementation="cygwin"
+	;;
+*)
+	AC_MSG_ERROR([HIDAPI is not supported on your operating system yet])
+esac
+
+LIBS_HIDRAW="${LIBS} ${LIBS_HIDRAW_PR}"
+LIBS_LIBUSB="${LIBS} ${LIBS_LIBUSB_PRIVATE}"
+AC_SUBST([LIBS_HIDRAW])
+AC_SUBST([LIBS_LIBUSB])
+AC_SUBST([CFLAGS_LIBUSB])
+AC_SUBST([CFLAGS_HIDRAW])
+
+if test "x$os" = xwindows; then
+	AC_DEFINE(OS_WINDOWS, 1, [Windows implementations])
+	AC_SUBST(OS_WINDOWS)
+	LDFLAGS="${LDFLAGS} -no-undefined"
+	LIBS="${LIBS} -lsetupapi"
+fi
+
+if test "x$threads" = xpthreads; then
+	AX_PTHREAD([found_pthreads=yes], [found_pthreads=no])
+
+	if test "x$found_pthreads" = xyes; then
+		if test "x$os" = xlinux; then
+			# Only use pthreads for libusb implementation on Linux.
+			LIBS_LIBUSB="$PTHREAD_LIBS $LIBS_LIBUSB"
+			CFLAGS_LIBUSB="$CFLAGS_LIBUSB $PTHREAD_CFLAGS"
+			# There's no separate CC on Linux for threading,
+			# so it's ok that both implementations use $PTHREAD_CC
+			CC="$PTHREAD_CC"
+		else
+			LIBS="$PTHREAD_LIBS $LIBS"
+			CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+			CC="$PTHREAD_CC"
+		fi
+	fi
+fi
+
+# Test GUI
+AC_ARG_ENABLE([testgui],
+	[AS_HELP_STRING([--enable-testgui],
+		[enable building of test GUI (default n)])],
+	[testgui_enabled=$enableval],
+	[testgui_enabled='no'])
+AM_CONDITIONAL([BUILD_TESTGUI], [test "x$testgui_enabled" != "xno"])
+
+# Configure the MacOS TestGUI app bundle
+rm -Rf testgui/TestGUI.app
+mkdir -p testgui/TestGUI.app
+cp -R ${srcdir}/testgui/TestGUI.app.in/* testgui/TestGUI.app
+chmod -R u+w testgui/TestGUI.app
+mkdir testgui/TestGUI.app/Contents/MacOS/
+
+if test "x$testgui_enabled" != "xno"; then
+	if test "x$os" = xdarwin; then
+		# On Mac OS, don't use pkg-config.
+		AC_CHECK_PROG([foxconfig], [fox-config], [fox-config], false)
+		if test "x$foxconfig" = "xfalse"; then
+			hidapi_prog_error fox-config "FOX Toolkit"
+		fi
+		LIBS_TESTGUI+=`$foxconfig --libs`
+		LIBS_TESTGUI+=" -framework Cocoa -L/usr/X11R6/lib"
+		CFLAGS_TESTGUI+=`$foxconfig --cflags`
+		OBJCFLAGS+=" -x objective-c++"
+	elif test "x$os" = xwindows; then
+		# On Windows, just set the paths for Fox toolkit
+		if test "x$win_implementation" = xmingw; then
+			CFLAGS_TESTGUI="-I\$(srcdir)/../../hidapi-externals/fox/include -g -c"
+			LIBS_TESTGUI=" -mwindows \$(srcdir)/../../hidapi-externals/fox/lib/libFOX-1.6.a -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32"
+		else
+			# Cygwin
+			CFLAGS_TESTGUI="-DWIN32 -I\$(srcdir)/../../hidapi-externals/fox/include -g -c"
+			LIBS_TESTGUI="\$(srcdir)/../../hidapi-externals/fox/lib/libFOX-cygwin-1.6.a -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32"
+		fi
+	else
+		# On Linux and FreeBSD platforms, use pkg-config to find fox.
+		PKG_CHECK_MODULES([fox], [fox17], [], [PKG_CHECK_MODULES([fox], [fox])])
+		LIBS_TESTGUI="${LIBS_TESTGUI} $fox_LIBS"
+		if test "x$os" = xfreebsd; then
+			LIBS_TESTGUI="${LIBS_TESTGUI} -L/usr/local/lib"
+		fi
+		CFLAGS_TESTGUI="${CFLAGS_TESTGUI} $fox_CFLAGS"
+	fi
+fi
+AC_SUBST([LIBS_TESTGUI])
+AC_SUBST([CFLAGS_TESTGUI])
+AC_SUBST([backend])
+
+# OS info for Automake
+AM_CONDITIONAL(OS_LINUX, test "x$os" = xlinux)
+AM_CONDITIONAL(OS_DARWIN, test "x$os" = xdarwin)
+AM_CONDITIONAL(OS_FREEBSD, test "x$os" = xfreebsd)
+AM_CONDITIONAL(OS_KFREEBSD, test "x$os" = xkfreebsd)
+AM_CONDITIONAL(OS_WINDOWS, test "x$os" = xwindows)
+
+AC_CONFIG_HEADERS([config.h])
+
+if test "x$os" = "xlinux"; then
+	AC_CONFIG_FILES([pc/hidapi-hidraw.pc])
+	AC_CONFIG_FILES([pc/hidapi-libusb.pc])
+else
+	AC_CONFIG_FILES([pc/hidapi.pc])
+fi
+
+AC_SUBST(LTLDFLAGS)
+
+AC_CONFIG_FILES([Makefile \
+	hidtest/Makefile \
+	libusb/Makefile \
+	linux/Makefile \
+	mac/Makefile \
+	testgui/Makefile \
+	windows/Makefile])
+AC_OUTPUT
diff --git a/source/src/hidapi/doxygen/Doxyfile b/source/src/hidapi/doxygen/Doxyfile
new file mode 100644
index 0000000..9d983e9
--- /dev/null
+++ b/source/src/hidapi/doxygen/Doxyfile
@@ -0,0 +1,1630 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = hidapi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = ../hidapi
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/source/src/hidapi/hidapi/hidapi.h b/source/src/hidapi/hidapi/hidapi.h
new file mode 100644
index 0000000..15d6323
--- /dev/null
+++ b/source/src/hidapi/hidapi/hidapi.h
@@ -0,0 +1,398 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Alan Ott
+ Signal 11 Software
+
+ 8/22/2009
+
+ Copyright 2009, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        http://github.com/signal11/hidapi .
+********************************************************/
+
+/** @file
+ * @defgroup API hidapi API
+ */
+
+#ifndef HIDAPI_H__
+#define HIDAPI_H__
+
+#include <wchar.h>
+
+#if defined(_WIN32) && !defined(NAMESPACE) && (0) /* SDL: don't export hidapi syms */
+      #define HID_API_EXPORT __declspec(dllexport)
+      #define HID_API_CALL
+#else
+      #define HID_API_EXPORT /**< API export macro */
+      #define HID_API_CALL /**< API call macro */
+#endif
+
+#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
+
+#if defined(__cplusplus) && !defined(NAMESPACE)
+extern "C" {
+#endif
+#ifdef NAMESPACE
+namespace NAMESPACE {
+#endif
+
+		struct hid_device_;
+		typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
+
+		/** hidapi info structure */
+		struct hid_device_info {
+			/** Platform-specific device path */
+			char *path;
+			/** Device Vendor ID */
+			unsigned short vendor_id;
+			/** Device Product ID */
+			unsigned short product_id;
+			/** Serial Number */
+			wchar_t *serial_number;
+			/** Device Release Number in binary-coded decimal,
+			    also known as Device Version Number */
+			unsigned short release_number;
+			/** Manufacturer String */
+			wchar_t *manufacturer_string;
+			/** Product string */
+			wchar_t *product_string;
+			/** Usage Page for this Device/Interface
+			    (Windows/Mac only). */
+			unsigned short usage_page;
+			/** Usage for this Device/Interface
+			    (Windows/Mac only).*/
+			unsigned short usage;
+			/** The USB interface which this logical device
+			    represents. Valid on both Linux implementations
+			    in all cases, and valid on the Windows implementation
+			    only if the device contains more than one interface. */
+			int interface_number;
+
+			/** Pointer to the next device */
+			struct hid_device_info *next;
+		};
+
+
+		/** @brief Initialize the HIDAPI library.
+
+			This function initializes the HIDAPI library. Calling it is not
+			strictly necessary, as it will be called automatically by
+			hid_enumerate() and any of the hid_open_*() functions if it is
+			needed.  This function should be called at the beginning of
+			execution however, if there is a chance of HIDAPI handles
+			being opened by different threads simultaneously.
+			
+			@ingroup API
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_init(void);
+
+		/** @brief Finalize the HIDAPI library.
+
+			This function frees all of the static data associated with
+			HIDAPI. It should be called at the end of execution to avoid
+			memory leaks.
+
+			@ingroup API
+
+		    @returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_exit(void);
+
+		/** @brief Enumerate the HID Devices.
+
+			This function returns a linked list of all the HID devices
+			attached to the system which match vendor_id and product_id.
+			If @p vendor_id is set to 0 then any vendor matches.
+			If @p product_id is set to 0 then any product matches.
+			If @p vendor_id and @p product_id are both set to 0, then
+			all HID devices will be returned.
+
+			@ingroup API
+			@param vendor_id The Vendor ID (VID) of the types of device
+				to open.
+			@param product_id The Product ID (PID) of the types of
+				device to open.
+
+		    @returns
+		    	This function returns a pointer to a linked list of type
+		    	struct #hid_device, containing information about the HID devices
+		    	attached to the system, or NULL in the case of failure. Free
+		    	this linked list by calling hid_free_enumeration().
+		*/
+		struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
+
+		/** @brief Free an enumeration Linked List
+
+		    This function frees a linked list created by hid_enumerate().
+
+			@ingroup API
+		    @param devs Pointer to a list of struct_device returned from
+		    	      hid_enumerate().
+		*/
+		void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
+
+		/** @brief Open a HID device using a Vendor ID (VID), Product ID
+			(PID) and optionally a serial number.
+
+			If @p serial_number is NULL, the first device with the
+			specified VID and PID is opened.
+
+			@ingroup API
+			@param vendor_id The Vendor ID (VID) of the device to open.
+			@param product_id The Product ID (PID) of the device to open.
+			@param serial_number The Serial Number of the device to open
+				               (Optionally NULL).
+
+			@returns
+				This function returns a pointer to a #hid_device object on
+				success or NULL on failure.
+		*/
+		HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
+
+		/** @brief Open a HID device by its path name.
+
+			The path name be determined by calling hid_enumerate(), or a
+			platform-specific path name can be used (eg: /dev/hidraw0 on
+			Linux).
+
+			@ingroup API
+		    @param path The path name of the device to open
+
+			@returns
+				This function returns a pointer to a #hid_device object on
+				success or NULL on failure.
+		*/
+		HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive /* = false */);
+
+		/** @brief Write an Output report to a HID device.
+
+			The first byte of @p data[] must contain the Report ID. For
+			devices which only support a single report, this must be set
+			to 0x0. The remaining bytes contain the report data. Since
+			the Report ID is mandatory, calls to hid_write() will always
+			contain one more byte than the report contains. For example,
+			if a hid report is 16 bytes long, 17 bytes must be passed to
+			hid_write(), the Report ID (or 0x0, for devices with a
+			single report), followed by the report data (16 bytes). In
+			this example, the length passed in would be 17.
+
+			hid_write() will send the data on the first OUT endpoint, if
+			one exists. If it does not, it will send the data through
+			the Control Endpoint (Endpoint 0).
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param data The data to send, including the report number as
+				the first byte.
+			@param length The length in bytes of the data to send.
+
+			@returns
+				This function returns the actual number of bytes written and
+				-1 on error.
+		*/
+		int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
+
+		/** @brief Read an Input report from a HID device with timeout.
+
+			Input reports are returned
+			to the host through the INTERRUPT IN endpoint. The first byte will
+			contain the Report number if the device uses numbered reports.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param data A buffer to put the read data into.
+			@param length The number of bytes to read. For devices with
+				multiple reports, make sure to read an extra byte for
+				the report number.
+			@param milliseconds timeout in milliseconds or -1 for blocking wait.
+
+			@returns
+				This function returns the actual number of bytes read and
+				-1 on error. If no packet was available to be read within
+				the timeout period, this function returns 0.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds);
+
+		/** @brief Read an Input report from a HID device.
+
+			Input reports are returned
+		    to the host through the INTERRUPT IN endpoint. The first byte will
+			contain the Report number if the device uses numbered reports.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param data A buffer to put the read data into.
+			@param length The number of bytes to read. For devices with
+				multiple reports, make sure to read an extra byte for
+				the report number.
+
+			@returns
+				This function returns the actual number of bytes read and
+				-1 on error. If no packet was available to be read and
+				the handle is in non-blocking mode, this function returns 0.
+		*/
+		int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
+
+		/** @brief Set the device handle to be non-blocking.
+
+			In non-blocking mode calls to hid_read() will return
+			immediately with a value of 0 if there is no data to be
+			read. In blocking mode, hid_read() will wait (block) until
+			there is data to read before returning.
+
+			Nonblocking can be turned on and off at any time.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param nonblock enable or not the nonblocking reads
+			 - 1 to enable nonblocking
+			 - 0 to disable nonblocking.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
+
+		/** @brief Send a Feature report to the device.
+
+			Feature reports are sent over the Control endpoint as a
+			Set_Report transfer.  The first byte of @p data[] must
+			contain the Report ID. For devices which only support a
+			single report, this must be set to 0x0. The remaining bytes
+			contain the report data. Since the Report ID is mandatory,
+			calls to hid_send_feature_report() will always contain one
+			more byte than the report contains. For example, if a hid
+			report is 16 bytes long, 17 bytes must be passed to
+			hid_send_feature_report(): the Report ID (or 0x0, for
+			devices which do not use numbered reports), followed by the
+			report data (16 bytes). In this example, the length passed
+			in would be 17.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param data The data to send, including the report number as
+				the first byte.
+			@param length The length in bytes of the data to send, including
+				the report number.
+
+			@returns
+				This function returns the actual number of bytes written and
+				-1 on error.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
+
+		/** @brief Get a feature report from a HID device.
+
+			Set the first byte of @p data[] to the Report ID of the
+			report to be read.  Make sure to allow space for this
+			extra byte in @p data[]. Upon return, the first byte will
+			still contain the Report ID, and the report data will
+			start in data[1].
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param data A buffer to put the read data into, including
+				the Report ID. Set the first byte of @p data[] to the
+				Report ID of the report to be read, or set it to zero
+				if your device does not use numbered reports.
+			@param length The number of bytes to read, including an
+				extra byte for the report ID. The buffer can be longer
+				than the actual report.
+
+			@returns
+				This function returns the number of bytes read plus
+				one for the report ID (which is still in the first
+				byte), or -1 on error.
+		*/
+		int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
+
+		/** @brief Close a HID device.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+		*/
+		void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
+
+		/** @brief Get The Manufacturer String from a HID device.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param string A wide string buffer to put the data into.
+			@param maxlen The length of the buffer in multiples of wchar_t.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
+
+		/** @brief Get The Product String from a HID device.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param string A wide string buffer to put the data into.
+			@param maxlen The length of the buffer in multiples of wchar_t.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
+
+		/** @brief Get The Serial Number String from a HID device.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param string A wide string buffer to put the data into.
+			@param maxlen The length of the buffer in multiples of wchar_t.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
+
+		/** @brief Get a string from a HID device, based on its string index.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+			@param string_index The index of the string to get.
+			@param string A wide string buffer to put the data into.
+			@param maxlen The length of the buffer in multiples of wchar_t.
+
+			@returns
+				This function returns 0 on success and -1 on error.
+		*/
+		int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
+
+		/** @brief Get a string describing the last error which occurred.
+
+			@ingroup API
+			@param device A device handle returned from hid_open().
+
+			@returns
+				This function returns a string containing the last error
+				which occurred or NULL if none has occurred.
+		*/
+		HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
+
+#if defined(__cplusplus) && !defined(NAMESPACE)
+}
+#endif
+#ifdef NAMESPACE
+}
+#endif
+
+#endif
+
diff --git a/source/src/hidapi/hidtest/Makefile.am b/source/src/hidapi/hidtest/Makefile.am
new file mode 100644
index 0000000..d278644
--- /dev/null
+++ b/source/src/hidapi/hidtest/Makefile.am
@@ -0,0 +1,20 @@
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
+
+## Linux
+if OS_LINUX
+noinst_PROGRAMS = hidtest-libusb hidtest-hidraw
+
+hidtest_hidraw_SOURCES = hidtest.cpp
+hidtest_hidraw_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la
+
+hidtest_libusb_SOURCES = hidtest.cpp
+hidtest_libusb_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la
+else
+
+# Other OS's
+noinst_PROGRAMS = hidtest
+
+hidtest_SOURCES = hidtest.cpp
+hidtest_LDADD = $(top_builddir)/$(backend)/libhidapi.la
+
+endif
diff --git a/source/src/hidapi/hidtest/hidtest.cpp b/source/src/hidapi/hidtest/hidtest.cpp
new file mode 100644
index 0000000..94f0a5c
--- /dev/null
+++ b/source/src/hidapi/hidtest/hidtest.cpp
@@ -0,0 +1,194 @@
+/*******************************************************
+ Windows HID simplification
+
+ Alan Ott
+ Signal 11 Software
+
+ 8/22/2009
+
+ Copyright 2009
+ 
+ This contents of this file may be used by anyone
+ for any reason without any conditions and may be
+ used as a starting point for your own applications
+ which use HIDAPI.
+********************************************************/
+
+#include <stdio.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+#include "hidapi.h"
+
+// Headers needed for sleeping.
+#ifdef _WIN32
+	#include <windows.h>
+#else
+	#include <unistd.h>
+#endif
+
+int main(int argc, char* argv[])
+{
+	int res;
+	unsigned char buf[256];
+	#define MAX_STR 255
+	wchar_t wstr[MAX_STR];
+	hid_device *handle;
+	int i;
+
+#ifdef WIN32
+	UNREFERENCED_PARAMETER(argc);
+	UNREFERENCED_PARAMETER(argv);
+#endif
+
+	struct hid_device_info *devs, *cur_dev;
+	
+	if (hid_init())
+		return -1;
+
+	devs = hid_enumerate(0x0, 0x0);
+	cur_dev = devs;	
+	while (cur_dev) {
+		printf("Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
+		printf("\n");
+		printf("  Manufacturer: %ls\n", cur_dev->manufacturer_string);
+		printf("  Product:      %ls\n", cur_dev->product_string);
+		printf("  Release:      %hx\n", cur_dev->release_number);
+		printf("  Interface:    %d\n",  cur_dev->interface_number);
+		printf("\n");
+		cur_dev = cur_dev->next;
+	}
+	hid_free_enumeration(devs);
+
+	// Set up the command buffer.
+	memset(buf,0x00,sizeof(buf));
+	buf[0] = 0x01;
+	buf[1] = 0x81;
+	
+
+	// Open the device using the VID, PID,
+	// and optionally the Serial number.
+	////handle = hid_open(0x4d8, 0x3f, L"12345");
+	handle = hid_open(0x4d8, 0x3f, NULL);
+	if (!handle) {
+		printf("unable to open device\n");
+ 		return 1;
+	}
+
+	// Read the Manufacturer String
+	wstr[0] = 0x0000;
+	res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
+	if (res < 0)
+		printf("Unable to read manufacturer string\n");
+	printf("Manufacturer String: %ls\n", wstr);
+
+	// Read the Product String
+	wstr[0] = 0x0000;
+	res = hid_get_product_string(handle, wstr, MAX_STR);
+	if (res < 0)
+		printf("Unable to read product string\n");
+	printf("Product String: %ls\n", wstr);
+
+	// Read the Serial Number String
+	wstr[0] = 0x0000;
+	res = hid_get_serial_number_string(handle, wstr, MAX_STR);
+	if (res < 0)
+		printf("Unable to read serial number string\n");
+	printf("Serial Number String: (%d) %ls", wstr[0], wstr);
+	printf("\n");
+
+	// Read Indexed String 1
+	wstr[0] = 0x0000;
+	res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
+	if (res < 0)
+		printf("Unable to read indexed string 1\n");
+	printf("Indexed String 1: %ls\n", wstr);
+
+	// Set the hid_read() function to be non-blocking.
+	hid_set_nonblocking(handle, 1);
+	
+	// Try to read from the device. There shoud be no
+	// data here, but execution should not block.
+	res = hid_read(handle, buf, 17);
+
+	// Send a Feature Report to the device
+	buf[0] = 0x2;
+	buf[1] = 0xa0;
+	buf[2] = 0x0a;
+	buf[3] = 0x00;
+	buf[4] = 0x00;
+	res = hid_send_feature_report(handle, buf, 17);
+	if (res < 0) {
+		printf("Unable to send a feature report.\n");
+	}
+
+	memset(buf,0,sizeof(buf));
+
+	// Read a Feature Report from the device
+	buf[0] = 0x2;
+	res = hid_get_feature_report(handle, buf, sizeof(buf));
+	if (res < 0) {
+		printf("Unable to get a feature report.\n");
+		printf("%ls", hid_error(handle));
+	}
+	else {
+		// Print out the returned buffer.
+		printf("Feature Report\n   ");
+		for (i = 0; i < res; i++)
+			printf("%02hhx ", buf[i]);
+		printf("\n");
+	}
+
+	memset(buf,0,sizeof(buf));
+
+	// Toggle LED (cmd 0x80). The first byte is the report number (0x1).
+	buf[0] = 0x1;
+	buf[1] = 0x80;
+	res = hid_write(handle, buf, 17);
+	if (res < 0) {
+		printf("Unable to write()\n");
+		printf("Error: %ls\n", hid_error(handle));
+	}
+	
+
+	// Request state (cmd 0x81). The first byte is the report number (0x1).
+	buf[0] = 0x1;
+	buf[1] = 0x81;
+	hid_write(handle, buf, 17);
+	if (res < 0)
+		printf("Unable to write() (2)\n");
+
+	// Read requested state. hid_read() has been set to be
+	// non-blocking by the call to hid_set_nonblocking() above.
+	// This loop demonstrates the non-blocking nature of hid_read().
+	res = 0;
+	while (res == 0) {
+		res = hid_read(handle, buf, sizeof(buf));
+		if (res == 0)
+			printf("waiting...\n");
+		if (res < 0)
+			printf("Unable to read()\n");
+		#ifdef WIN32
+		Sleep(500);
+		#else
+		usleep(500*1000);
+		#endif
+	}
+
+	printf("Data read:\n   ");
+	// Print out the returned buffer.
+	for (i = 0; i < res; i++)
+		printf("%02hhx ", buf[i]);
+	printf("\n");
+
+	hid_close(handle);
+
+	/* Free static HIDAPI objects. */
+	hid_exit();
+
+#ifdef WIN32
+	system("pause");
+#endif
+
+	return 0;
+}
diff --git a/source/src/hidapi/ios/Makefile-manual b/source/src/hidapi/ios/Makefile-manual
new file mode 100644
index 0000000..939a077
--- /dev/null
+++ b/source/src/hidapi/ios/Makefile-manual
@@ -0,0 +1,32 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-07-03
+###########################################
+
+all: hidtest
+
+CC=gcc
+CXX=g++
+COBJS=hid.o
+CPPOBJS=../hidtest/hidtest.o
+OBJS=$(COBJS) $(CPPOBJS)
+CFLAGS+=-I../hidapi -Wall -g -c 
+LIBS=-framework CoreBluetooth -framework CoreFoundation
+
+
+hidtest: $(OBJS)
+	g++ -Wall -g $^ $(LIBS) -o hidtest
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+clean:
+	rm -f *.o hidtest $(CPPOBJS)
+
+.PHONY: clean
diff --git a/source/src/hidapi/ios/Makefile.am b/source/src/hidapi/ios/Makefile.am
new file mode 100644
index 0000000..1f8f2ce
--- /dev/null
+++ b/source/src/hidapi/ios/Makefile.am
@@ -0,0 +1,9 @@
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.m
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
+
+hdrdir = $(includedir)/hidapi
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+
+EXTRA_DIST = Makefile-manual
diff --git a/source/src/hidapi/ios/hid.m b/source/src/hidapi/ios/hid.m
new file mode 100644
index 0000000..a0ca7cd
--- /dev/null
+++ b/source/src/hidapi/ios/hid.m
@@ -0,0 +1,914 @@
+//======== Copyright (c) 2017 Valve Corporation, All rights reserved. =========
+//
+// Purpose: HID device abstraction temporary stub
+//
+//=============================================================================
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include <CoreBluetooth/CoreBluetooth.h>
+#include <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+#import <mach/mach_time.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include "../hidapi/hidapi.h"
+
+#define VALVE_USB_VID       0x28DE
+#define D0G_BLE2_PID        0x1106
+
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+// enables detailed NSLog logging of feature reports
+#define FEATURE_REPORT_LOGGING	0
+
+#define REPORT_SEGMENT_DATA_FLAG	0x80
+#define REPORT_SEGMENT_LAST_FLAG	0x40
+
+#define VALVE_SERVICE		@"100F6C32-1735-4313-B402-38567131E5F3"
+
+// (READ/NOTIFICATIONS)
+#define VALVE_INPUT_CHAR	@"100F6C33-1735-4313-B402-38567131E5F3"
+
+//  (READ/WRITE)
+#define VALVE_REPORT_CHAR	@"100F6C34-1735-4313-B402-38567131E5F3"
+
+// TODO: create CBUUID's in __attribute__((constructor)) rather than doing [CBUUID UUIDWithString:...] everywhere
+
+#pragma pack(push,1)
+
+typedef struct
+{
+	uint8_t		segmentHeader;
+	uint8_t		featureReportMessageID;
+	uint8_t		length;
+	uint8_t		settingIdentifier;
+	union {
+		uint16_t	usPayload;
+		uint32_t	uPayload;
+		uint64_t	ulPayload;
+		uint8_t		ucPayload[15];
+	};
+} bluetoothSegment;
+
+typedef struct {
+	uint8_t		id;
+	union {
+		bluetoothSegment segment;
+		struct {
+			uint8_t		segmentHeader;
+			uint8_t		featureReportMessageID;
+			uint8_t		length;
+			uint8_t		settingIdentifier;
+			union {
+				uint16_t	usPayload;
+				uint32_t	uPayload;
+				uint64_t	ulPayload;
+				uint8_t		ucPayload[15];
+			};
+		};
+	};
+} hidFeatureReport;
+
+#pragma pack(pop)
+
+size_t GetBluetoothSegmentSize(bluetoothSegment *segment)
+{
+    return segment->length + 3;
+}
+
+#define RingBuffer_cbElem   19
+#define RingBuffer_nElem    4096
+
+typedef struct {
+	int _first, _last;
+	uint8_t _data[ ( RingBuffer_nElem * RingBuffer_cbElem ) ];
+	pthread_mutex_t accessLock;
+} RingBuffer;
+
+static void RingBuffer_init( RingBuffer *this )
+{
+    this->_first = -1;
+    this->_last = 0;
+    pthread_mutex_init( &this->accessLock, 0 );
+}
+	
+static bool RingBuffer_write( RingBuffer *this, const uint8_t *src )
+{
+    pthread_mutex_lock( &this->accessLock );
+    memcpy( &this->_data[ this->_last ], src, RingBuffer_cbElem );
+    if ( this->_first == -1 )
+    {
+        this->_first = this->_last;
+    }
+    this->_last = ( this->_last + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
+    if ( this->_last == this->_first )
+    {
+        this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
+        pthread_mutex_unlock( &this->accessLock );
+        return false;
+    }
+    pthread_mutex_unlock( &this->accessLock );
+    return true;
+}
+
+static bool RingBuffer_read( RingBuffer *this, uint8_t *dst )
+{
+    pthread_mutex_lock( &this->accessLock );
+    if ( this->_first == -1 )
+    {
+        pthread_mutex_unlock( &this->accessLock );
+        return false;
+    }
+    memcpy( dst, &this->_data[ this->_first ], RingBuffer_cbElem );
+    this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem);
+    if ( this->_first == this->_last )
+    {
+        this->_first = -1;
+    }
+    pthread_mutex_unlock( &this->accessLock );
+    return true;
+}
+
+
+#pragma mark HIDBLEDevice Definition
+
+typedef enum
+{
+	BLEDeviceWaitState_None,
+	BLEDeviceWaitState_Waiting,
+	BLEDeviceWaitState_Complete,
+	BLEDeviceWaitState_Error
+} BLEDeviceWaitState;
+
+@interface HIDBLEDevice : NSObject <CBPeripheralDelegate>
+{
+	RingBuffer _inputReports;
+	uint8_t	_featureReport[20];
+	BLEDeviceWaitState	_waitStateForReadFeatureReport;
+	BLEDeviceWaitState	_waitStateForWriteFeatureReport;
+}
+
+@property (nonatomic, readwrite) bool connected;
+@property (nonatomic, readwrite) bool ready;
+
+@property (nonatomic, strong) CBPeripheral     *bleSteamController;
+@property (nonatomic, strong) CBCharacteristic *bleCharacteristicInput;
+@property (nonatomic, strong) CBCharacteristic *bleCharacteristicReport;
+
+- (id)initWithPeripheral:(CBPeripheral *)peripheral;
+
+@end
+
+
+@interface HIDBLEManager : NSObject <CBCentralManagerDelegate>
+
+@property (nonatomic) int nPendingScans;
+@property (nonatomic) int nPendingPairs;
+@property (nonatomic, strong) CBCentralManager *centralManager;
+@property (nonatomic, strong) NSMapTable<CBPeripheral *, HIDBLEDevice *> *deviceMap;
+@property (nonatomic, retain) dispatch_queue_t bleSerialQueue;
+
++ (instancetype)sharedInstance;
+- (void)startScan:(int)duration;
+- (void)stopScan;
+- (int)updateConnectedSteamControllers:(BOOL) bForce;
+- (void)appWillResignActiveNotification:(NSNotification *)note;
+- (void)appDidBecomeActiveNotification:(NSNotification *)note;
+
+@end
+
+
+// singleton class - access using HIDBLEManager.sharedInstance
+@implementation HIDBLEManager
+
++ (instancetype)sharedInstance
+{
+	static HIDBLEManager *sharedInstance = nil;
+	static dispatch_once_t onceToken;
+	dispatch_once(&onceToken, ^{
+		sharedInstance = [HIDBLEManager new];
+		sharedInstance.nPendingScans = 0;
+		sharedInstance.nPendingPairs = 0;
+		
+		[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appWillResignActiveNotification:) name: UIApplicationWillResignActiveNotification object:nil];
+		[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appDidBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
+
+		// receive reports on a high-priority serial-queue. optionally put writes on the serial queue to avoid logical
+		// race conditions talking to the controller from multiple threads, although BLE fragmentation/assembly means
+		// that we can still screw this up.
+		// most importantly we need to consume reports at a high priority to avoid the OS thinking we aren't really
+		// listening to the BLE device, as iOS on slower devices may stop delivery of packets to the app WITHOUT ACTUALLY
+		// DISCONNECTING FROM THE DEVICE if we don't react quickly enough to their delivery.
+		// see also the error-handling states in the peripheral delegate to re-open the device if it gets closed
+		sharedInstance.bleSerialQueue = dispatch_queue_create( "com.valvesoftware.steamcontroller.ble", DISPATCH_QUEUE_SERIAL );
+		dispatch_set_target_queue( sharedInstance.bleSerialQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
+
+		// creating a CBCentralManager will always trigger a future centralManagerDidUpdateState:
+		// where any scanning gets started or connecting to existing peripherals happens, it's never already in a
+		// powered-on state for a newly launched application.
+		sharedInstance.centralManager = [[CBCentralManager alloc] initWithDelegate:sharedInstance queue:sharedInstance.bleSerialQueue];
+		sharedInstance.deviceMap = [[NSMapTable alloc] initWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory capacity:4];
+	});
+	return sharedInstance;
+}
+
+// called for NSNotification UIApplicationWillResignActiveNotification
+- (void)appWillResignActiveNotification:(NSNotification *)note
+{
+	// we'll get resign-active notification if pairing is happening.
+	if ( self.nPendingPairs > 0 )
+		return;
+
+	for ( CBPeripheral *peripheral in self.deviceMap )
+	{
+		HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
+		if ( steamController )
+		{
+			steamController.connected = NO;
+			steamController.ready = NO;
+			[self.centralManager cancelPeripheralConnection:peripheral];
+		}
+	}
+	[self.deviceMap removeAllObjects];
+}
+
+// called for NSNotification UIApplicationDidBecomeActiveNotification
+//  whenever the application comes back from being inactive, trigger a 20s pairing scan and reconnect
+//  any devices that may have paired while we were inactive.
+- (void)appDidBecomeActiveNotification:(NSNotification *)note
+{
+	[self updateConnectedSteamControllers:true];
+	[self startScan:20];
+}
+
+- (int)updateConnectedSteamControllers:(BOOL) bForce
+{
+	static uint64_t s_unLastUpdateTick = 0;
+	static mach_timebase_info_data_t s_timebase_info;
+	
+	if (s_timebase_info.denom == 0)
+	{
+		mach_timebase_info( &s_timebase_info );
+	}
+	
+	uint64_t ticksNow = mach_approximate_time();
+	if ( !bForce && ( ( (ticksNow - s_unLastUpdateTick) * s_timebase_info.numer ) / s_timebase_info.denom ) < (5ull * NSEC_PER_SEC) )
+		return (int)self.deviceMap.count;
+	
+	// we can see previously connected BLE peripherals but can't connect until the CBCentralManager
+	// is fully powered up - only do work when we are in that state
+	if ( self.centralManager.state != CBManagerStatePoweredOn )
+		return (int)self.deviceMap.count;
+
+	// only update our last-check-time if we actually did work, otherwise there can be a long delay during initial power-up
+	s_unLastUpdateTick = mach_approximate_time();
+	
+	// if a pair is in-flight, the central manager may still give it back via retrieveConnected... and
+	// cause the SDL layer to attempt to initialize it while some of its endpoints haven't yet been established
+	if ( self.nPendingPairs > 0 )
+		return (int)self.deviceMap.count;
+
+	NSArray<CBPeripheral *> *peripherals = [self.centralManager retrieveConnectedPeripheralsWithServices: @[ [CBUUID UUIDWithString:@"180A"]]];
+	for ( CBPeripheral *peripheral in peripherals )
+	{
+		// we already know this peripheral
+		if ( [self.deviceMap objectForKey: peripheral] != nil )
+			continue;
+		
+		NSLog( @"connected peripheral: %@", peripheral );
+		if ( [peripheral.name isEqualToString:@"SteamController"] )
+		{
+			HIDBLEDevice *steamController = [[HIDBLEDevice alloc] initWithPeripheral:peripheral];
+			[self.deviceMap setObject:steamController forKey:peripheral];
+			[self.centralManager connectPeripheral:peripheral options:nil];
+		}
+	}
+
+	return (int)self.deviceMap.count;
+}
+
+// manual API for folks to start & stop scanning
+- (void)startScan:(int)duration
+{
+	NSLog( @"BLE: requesting scan for %d seconds", duration );
+	@synchronized (self)
+	{
+		if ( _nPendingScans++ == 0 )
+		{
+			[self.centralManager scanForPeripheralsWithServices:nil options:nil];
+		}
+	}
+
+	if ( duration != 0 )
+	{
+		dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+			[self stopScan];
+		});
+	}
+}
+
+- (void)stopScan
+{
+	NSLog( @"BLE: stopping scan" );
+	@synchronized (self)
+	{
+		if ( --_nPendingScans <= 0 )
+		{
+			_nPendingScans = 0;
+			[self.centralManager stopScan];
+		}
+	}
+}
+
+
+#pragma mark CBCentralManagerDelegate Implementation
+
+// called whenever the BLE hardware state changes.
+- (void)centralManagerDidUpdateState:(CBCentralManager *)central
+{
+	switch ( central.state )
+	{
+		case CBCentralManagerStatePoweredOn:
+		{
+			NSLog( @"CoreBluetooth BLE hardware is powered on and ready" );
+			
+			// at startup, if we have no already attached peripherals, do a 20s scan for new unpaired devices,
+			// otherwise callers should occaisionally do additional scans. we don't want to continuously be
+			// scanning because it drains battery, causes other nearby people to have a hard time pairing their
+			// Steam Controllers, and may also trigger firmware weirdness when a device attempts to start
+			// the pairing sequence multiple times concurrently
+			if ( [self updateConnectedSteamControllers:false] == 0 )
+			{
+				// TODO: we could limit our scan to only peripherals supporting the SteamController service, but
+				//  that service doesn't currently fit in the base advertising packet, we'd need to put it into an
+				//  extended scan packet. Useful optimization downstream, but not currently necessary
+				//	NSArray *services = @[[CBUUID UUIDWithString:VALVE_SERVICE]];
+				[self startScan:20];
+			}
+			break;
+		}
+			
+		case CBCentralManagerStatePoweredOff:
+			NSLog( @"CoreBluetooth BLE hardware is powered off" );
+			break;
+			
+		case CBCentralManagerStateUnauthorized:
+			NSLog( @"CoreBluetooth BLE state is unauthorized" );
+			break;
+			
+		case CBCentralManagerStateUnknown:
+			NSLog( @"CoreBluetooth BLE state is unknown" );
+			break;
+			
+		case CBCentralManagerStateUnsupported:
+			NSLog( @"CoreBluetooth BLE hardware is unsupported on this platform" );
+			break;
+		
+		case CBCentralManagerStateResetting:
+			NSLog( @"CoreBluetooth BLE manager is resetting" );
+			break;
+	}
+}
+
+- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
+{
+	HIDBLEDevice *steamController = [_deviceMap objectForKey:peripheral];
+	steamController.connected = YES;
+	self.nPendingPairs -= 1;
+}
+
+- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
+{
+	NSLog( @"Failed to connect: %@", error );
+	[_deviceMap removeObjectForKey:peripheral];
+	self.nPendingPairs -= 1;
+}
+
+- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
+{
+	NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
+	NSString *log = [NSString stringWithFormat:@"Found '%@'", localName];
+	
+	if ( [localName isEqualToString:@"SteamController"] )
+	{
+		NSLog( @"%@ : %@ - %@", log, peripheral, advertisementData );
+		self.nPendingPairs += 1;
+		HIDBLEDevice *steamController = [[HIDBLEDevice alloc] initWithPeripheral:peripheral];
+		[self.deviceMap setObject:steamController forKey:peripheral];
+		[self.centralManager connectPeripheral:peripheral options:nil];
+	}
+}
+
+- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
+{
+	HIDBLEDevice *steamController = [self.deviceMap objectForKey:peripheral];
+	if ( steamController )
+	{
+		steamController.connected = NO;
+		steamController.ready = NO;
+		[self.deviceMap removeObjectForKey:peripheral];
+	}
+}
+
+@end
+
+
+// Core Bluetooth devices calling back on event boundaries of their run-loops. so annoying.
+static void process_pending_events()
+{
+	CFRunLoopRunResult res;
+	do
+	{
+		res = CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0.001, FALSE );
+	}
+	while( res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut );
+}
+
+@implementation HIDBLEDevice
+
+- (id)init
+{
+	if ( self = [super init] )
+	{
+        RingBuffer_init( &_inputReports );
+		self.bleSteamController = nil;
+		self.bleCharacteristicInput = nil;
+		self.bleCharacteristicReport = nil;
+		_connected = NO;
+		_ready = NO;
+	}
+	return self;
+}
+
+- (id)initWithPeripheral:(CBPeripheral *)peripheral
+{
+	if ( self = [super init] )
+	{
+        RingBuffer_init( &_inputReports );
+		_connected = NO;
+		_ready = NO;
+		self.bleSteamController = peripheral;
+		if ( peripheral )
+		{
+			peripheral.delegate = self;
+		}
+		self.bleCharacteristicInput = nil;
+		self.bleCharacteristicReport = nil;
+	}
+	return self;
+}
+
+- (void)setConnected:(bool)connected
+{
+	_connected = connected;
+	if ( _connected )
+	{
+		[_bleSteamController discoverServices:nil];
+	}
+	else
+	{
+		NSLog( @"Disconnected" );
+	}
+}
+
+- (size_t)read_input_report:(uint8_t *)dst
+{
+	if ( RingBuffer_read( &_inputReports, dst+1 ) )
+	{
+		*dst = 0x03;
+		return 20;
+	}
+	return 0;
+}
+
+- (int)send_report:(const uint8_t *)data length:(size_t)length
+{
+	[_bleSteamController writeValue:[NSData dataWithBytes:data length:length] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
+	return (int)length;
+}
+
+- (int)send_feature_report:(hidFeatureReport *)report
+{
+#if FEATURE_REPORT_LOGGING
+	uint8_t *reportBytes = (uint8_t *)report;
+	
+	NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", GetBluetoothSegmentSize( report->segment ),
+		  reportBytes[1], reportBytes[2], reportBytes[3], reportBytes[4], reportBytes[5], reportBytes[6],
+		  reportBytes[7], reportBytes[8], reportBytes[9], reportBytes[10], reportBytes[11], reportBytes[12],
+		  reportBytes[13], reportBytes[14], reportBytes[15], reportBytes[16], reportBytes[17], reportBytes[18],
+		  reportBytes[19] );
+#endif
+
+	int sendSize = (int)GetBluetoothSegmentSize( &report->segment );
+	if ( sendSize > 20 )
+		sendSize = 20;
+
+#if 1
+	// fire-and-forget - we are going to not wait for the response here because all Steam Controller BLE send_feature_report's are ignored,
+	//  except errors.
+	[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
+	
+	// pretend we received a result anybody cares about
+	return 19;
+
+#else
+	// this is technically the correct send_feature_report logic if you want to make sure it gets through and is
+	// acknowledged or errors out
+	_waitStateForWriteFeatureReport = BLEDeviceWaitState_Waiting;
+	[_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize
+									 ] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse];
+	
+	while ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Waiting )
+	{
+		process_pending_events();
+	}
+	
+	if ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Error )
+	{
+		_waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
+		return -1;
+	}
+	
+	_waitStateForWriteFeatureReport = BLEDeviceWaitState_None;
+	return 19;
+#endif
+}
+
+- (int)get_feature_report:(uint8_t)feature into:(uint8_t *)buffer
+{
+	_waitStateForReadFeatureReport = BLEDeviceWaitState_Waiting;
+	[_bleSteamController readValueForCharacteristic:_bleCharacteristicReport];
+	
+	while ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Waiting )
+		process_pending_events();
+	
+	if ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Error )
+	{
+		_waitStateForReadFeatureReport = BLEDeviceWaitState_None;
+		return -1;
+	}
+	
+	memcpy( buffer, _featureReport, sizeof(_featureReport) );
+	
+	_waitStateForReadFeatureReport = BLEDeviceWaitState_None;
+	
+#if FEATURE_REPORT_LOGGING
+	NSLog( @"HIDBLE:get_feature_report (19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]",
+		  buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6],
+		  buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12],
+		  buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18],
+		  buffer[19] );
+#endif
+
+	return 19;
+}
+
+#pragma mark CBPeripheralDelegate Implementation
+
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
+{
+	for (CBService *service in peripheral.services)
+	{
+		NSLog( @"Found Service: %@", service );
+		if ( [service.UUID isEqual:[CBUUID UUIDWithString:VALVE_SERVICE]] )
+		{
+			[peripheral discoverCharacteristics:nil forService:service];
+		}
+	}
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
+{
+	// nothing yet needed here, enable for logging
+	if ( /* DISABLES CODE */ (0) )
+	{
+		for ( CBDescriptor *descriptor in characteristic.descriptors )
+		{
+			NSLog( @" - Descriptor '%@'", descriptor );
+		}
+	}
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
+{
+	if ([service.UUID isEqual:[CBUUID UUIDWithString:VALVE_SERVICE]])
+	{
+		for (CBCharacteristic *aChar in service.characteristics)
+		{
+			NSLog( @"Found Characteristic %@", aChar );
+			
+			if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_INPUT_CHAR]] )
+			{
+				self.bleCharacteristicInput = aChar;
+			}
+			else if ( [aChar.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
+			{
+				self.bleCharacteristicReport = aChar;
+				[self.bleSteamController discoverDescriptorsForCharacteristic: aChar];
+			}
+		}
+	}
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
+{
+	static uint64_t s_ticksLastOverflowReport = 0;
+
+	// receiving an input report is the final indicator that the user accepted a pairing
+	// request and that we successfully established notification. CoreBluetooth has no
+	// notification of the pairing acknowledgement, which is a bad oversight.
+	if ( self.ready == NO )
+	{
+		self.ready = YES;
+		HIDBLEManager.sharedInstance.nPendingPairs -= 1;
+	}
+
+	if ( [characteristic.UUID isEqual:_bleCharacteristicInput.UUID] )
+	{
+		NSData *data = [characteristic value];
+		if ( data.length != 19 )
+		{
+			NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 19", (unsigned long)data.length );
+		}
+		if ( !RingBuffer_write( &_inputReports, (const uint8_t *)data.bytes ) )
+		{
+			uint64_t ticksNow = mach_approximate_time();
+			if ( ticksNow - s_ticksLastOverflowReport > (5ull * NSEC_PER_SEC / 10) )
+			{
+				NSLog( @"HIDBLE: input report buffer overflow" );
+				s_ticksLastOverflowReport = ticksNow;
+			}
+		}
+	}
+	else if ( [characteristic.UUID isEqual:_bleCharacteristicReport.UUID] )
+	{
+		memset( _featureReport, 0, sizeof(_featureReport) );
+		
+		if ( error != nil )
+		{
+			NSLog( @"HIDBLE: get_feature_report error: %@", error );
+			_waitStateForReadFeatureReport = BLEDeviceWaitState_Error;
+		}
+		else
+		{
+			NSData *data = [characteristic value];
+			if ( data.length != 20 )
+			{
+				NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 20", (unsigned long)data.length );
+			}
+			memcpy( _featureReport, data.bytes, MIN( data.length, sizeof(_featureReport) ) );
+			_waitStateForReadFeatureReport = BLEDeviceWaitState_Complete;
+		}
+	}
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
+{
+	if ( [characteristic.UUID isEqual:[CBUUID UUIDWithString:VALVE_REPORT_CHAR]] )
+	{
+		if ( error != nil )
+		{
+			NSLog( @"HIDBLE: write_feature_report error: %@", error );
+			_waitStateForWriteFeatureReport = BLEDeviceWaitState_Error;
+		}
+		else
+		{
+			_waitStateForWriteFeatureReport = BLEDeviceWaitState_Complete;
+		}
+	}
+}
+
+- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
+{
+	NSLog( @"didUpdateNotifcationStateForCharacteristic %@ (%@)", characteristic, error );
+}
+
+@end
+
+
+#pragma mark hid_api implementation
+
+struct hid_device_ {
+	void *device_handle;
+	int blocking;
+	hid_device *next;
+};
+
+int HID_API_EXPORT HID_API_CALL hid_init(void)
+{
+	return ( HIDBLEManager.sharedInstance == nil ) ? -1 : 0;
+}
+
+int HID_API_EXPORT HID_API_CALL hid_exit(void)
+{
+	return 0;
+}
+
+void HID_API_EXPORT HID_API_CALL hid_ble_scan( bool bStart )
+{
+	HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
+	if ( bStart )
+	{
+		[bleManager startScan:0];
+	}
+	else
+	{
+		[bleManager stopScan];
+	}
+}
+
+hid_device * HID_API_EXPORT hid_open_path( const char *path, int bExclusive /* = false */ )
+{
+	hid_device *result = NULL;
+	NSString *nssPath = [NSString stringWithUTF8String:path];
+	HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
+	NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
+	
+	for ( HIDBLEDevice *device in devices )
+	{
+		// we have the device but it hasn't found its service or characteristics until it is connected
+		if ( !device.ready || !device.connected || !device.bleCharacteristicInput )
+			continue;
+		
+		if ( [device.bleSteamController.identifier.UUIDString isEqualToString:nssPath] )
+		{
+			result = (hid_device *)malloc( sizeof( hid_device ) );
+			memset( result, 0, sizeof( hid_device ) );
+			result->device_handle = (void*)CFBridgingRetain( device );
+			result->blocking = NO;
+			// enable reporting input events on the characteristic
+			[device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput];
+			return result;
+		}
+	}
+	return result;
+}
+
+void  HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
+{
+	/* This function is identical to the Linux version. Platform independent. */
+	struct hid_device_info *d = devs;
+	while (d) {
+		struct hid_device_info *next = d->next;
+		free(d->path);
+		free(d->serial_number);
+		free(d->manufacturer_string);
+		free(d->product_string);
+		free(d);
+		d = next;
+	}
+}
+
+int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
+{
+	/* All Nonblocking operation is handled by the library. */
+	dev->blocking = !nonblock;
+	
+	return 0;
+}
+
+struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{ @autoreleasepool {
+	struct hid_device_info *root = NULL;
+	
+	if ( ( vendor_id == 0 && product_id == 0 ) ||
+		 ( vendor_id == VALVE_USB_VID && product_id == D0G_BLE2_PID ) )
+	{
+		HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
+		[bleManager updateConnectedSteamControllers:false];
+		NSEnumerator<HIDBLEDevice *> *devices = [bleManager.deviceMap objectEnumerator];
+		for ( HIDBLEDevice *device in devices )
+		{
+			// there are several brief windows in connecting to an already paired device and
+			// one long window waiting for users to confirm pairing where we don't want
+			// to consider a device ready - if we hand it back to SDL or another
+			// Steam Controller consumer, their additional SC setup work will fail
+			// in unusual/silent ways and we can actually corrupt the BLE stack for
+			// the entire system and kill the appletv remote's Menu button (!)
+			if ( device.bleSteamController.state != CBPeripheralStateConnected ||
+				 device.connected == NO || device.ready == NO )
+			{
+				if ( device.ready == NO && device.bleCharacteristicInput != nil )
+				{
+					// attempt to register for input reports. this call will silently fail
+					// until the pairing finalizes with user acceptance. oh, apple.
+					[device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput];
+				}
+				continue;
+			}
+			struct hid_device_info *device_info = (struct hid_device_info *)malloc( sizeof(struct hid_device_info) );
+			memset( device_info, 0, sizeof(struct hid_device_info) );
+			device_info->next = root;
+			root = device_info;
+			device_info->path = strdup( device.bleSteamController.identifier.UUIDString.UTF8String );
+			device_info->vendor_id = VALVE_USB_VID;
+			device_info->product_id = D0G_BLE2_PID;
+			device_info->product_string = wcsdup( L"Steam Controller" );
+			device_info->manufacturer_string = wcsdup( L"Valve Corporation" );
+		}
+	}
+	return root;
+}}
+
+int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	static wchar_t s_wszManufacturer[] = L"Valve Corporation";
+	wcsncpy( string, s_wszManufacturer, sizeof(s_wszManufacturer)/sizeof(s_wszManufacturer[0]) );
+	return 0;
+}
+
+int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	static wchar_t s_wszProduct[] = L"Steam Controller";
+	wcsncpy( string, s_wszProduct, sizeof(s_wszProduct)/sizeof(s_wszProduct[0]) );
+	return 0;
+}
+
+int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	static wchar_t s_wszSerial[] = L"12345";
+	wcsncpy( string, s_wszSerial, sizeof(s_wszSerial)/sizeof(s_wszSerial[0]) );
+	return 0;
+}
+
+int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
+{
+    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
+
+	if ( !device_handle.connected )
+		return -1;
+
+	return [device_handle send_report:data length:length];
+}
+
+void HID_API_EXPORT hid_close(hid_device *dev)
+{
+    HIDBLEDevice *device_handle = CFBridgingRelease( dev->device_handle );
+
+	// disable reporting input events on the characteristic
+	if ( device_handle.connected ) {
+		[device_handle.bleSteamController setNotifyValue:NO forCharacteristic:device_handle.bleCharacteristicInput];
+	}
+
+	free( dev );
+}
+
+int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
+{
+    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
+
+	if ( !device_handle.connected )
+		return -1;
+
+	return [device_handle send_feature_report:(hidFeatureReport *)(void *)data];
+}
+
+int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+{
+    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
+
+	if ( !device_handle.connected )
+		return -1;
+
+	size_t written = [device_handle get_feature_report:data[0] into:data];
+	
+	return written == length-1 ? (int)length : (int)written;
+}
+
+int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
+{
+    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
+
+	if ( !device_handle.connected )
+		return -1;
+
+	return hid_read_timeout(dev, data, length, 0);
+}
+
+int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
+{
+    HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
+
+	if ( !device_handle.connected )
+		return -1;
+	
+	if ( milliseconds != 0 )
+	{
+		NSLog( @"hid_read_timeout with non-zero wait" );
+	}
+	int result = (int)[device_handle read_input_report:data];
+#if FEATURE_REPORT_LOGGING
+	NSLog( @"HIDBLE:hid_read_timeout (%d) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", result,
+		  data[1], data[2], data[3], data[4], data[5], data[6],
+		  data[7], data[8], data[9], data[10], data[11], data[12],
+		  data[13], data[14], data[15], data[16], data[17], data[18],
+		  data[19] );
+#endif
+	return result;
+}
+
+#endif /* SDL_JOYSTICK_HIDAPI */
diff --git a/source/src/hidapi/libusb/Makefile-manual b/source/src/hidapi/libusb/Makefile-manual
new file mode 100644
index 0000000..c0fe868
--- /dev/null
+++ b/source/src/hidapi/libusb/Makefile-manual
@@ -0,0 +1,18 @@
+
+
+OS=$(shell uname)
+
+ifeq ($(OS), Linux)
+	FILE=Makefile.linux
+endif
+
+ifeq ($(OS), FreeBSD)
+	FILE=Makefile.freebsd
+endif
+
+ifeq ($(FILE), )
+all:
+	$(error Your platform ${OS} is not supported by hidapi/libusb at this time.)
+endif
+
+include $(FILE)
diff --git a/source/src/hidapi/libusb/Makefile.am b/source/src/hidapi/libusb/Makefile.am
new file mode 100644
index 0000000..13c9d35
--- /dev/null
+++ b/source/src/hidapi/libusb/Makefile.am
@@ -0,0 +1,27 @@
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi $(CFLAGS_LIBUSB)
+
+if OS_LINUX
+lib_LTLIBRARIES = libhidapi-libusb.la
+libhidapi_libusb_la_SOURCES = hid.c
+libhidapi_libusb_la_LDFLAGS = $(LTLDFLAGS) $(PTHREAD_CFLAGS)
+libhidapi_libusb_la_LIBADD = $(LIBS_LIBUSB)
+endif
+
+if OS_FREEBSD
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.c
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+libhidapi_la_LIBADD = $(LIBS_LIBUSB)
+endif
+
+if OS_KFREEBSD
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.c
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+libhidapi_la_LIBADD = $(LIBS_LIBUSB)
+endif
+
+hdrdir = $(includedir)/hidapi
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+
+EXTRA_DIST = Makefile-manual
diff --git a/source/src/hidapi/libusb/Makefile.freebsd b/source/src/hidapi/libusb/Makefile.freebsd
new file mode 100644
index 0000000..5e69e77
--- /dev/null
+++ b/source/src/hidapi/libusb/Makefile.freebsd
@@ -0,0 +1,46 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidtest libs
+
+libs: libhidapi.so
+
+CC       ?= cc
+CFLAGS   ?= -Wall -g -fPIC
+
+CXX      ?= c++
+CXXFLAGS ?= -Wall -g
+
+COBJS     = hid.o
+CPPOBJS   = ../hidtest/hidtest.o
+OBJS      = $(COBJS) $(CPPOBJS)
+INCLUDES  = -I../hidapi -I/usr/local/include
+LDFLAGS   = -L/usr/local/lib
+LIBS      = -lusb -liconv -pthread
+
+
+# Console Test Program
+hidtest: $(OBJS)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)
+
+# Shared Libs
+libhidapi.so: $(COBJS)
+	$(CC) $(LDFLAGS) -shared -Wl,-soname,$@.0 $^ -o $@ $(LIBS)
+
+# Objects
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
+
+
+clean:
+	rm -f $(OBJS) hidtest libhidapi.so ../hidtest/hidtest.o
+
+.PHONY: clean libs
diff --git a/source/src/hidapi/libusb/Makefile.linux b/source/src/hidapi/libusb/Makefile.linux
new file mode 100644
index 0000000..337b556
--- /dev/null
+++ b/source/src/hidapi/libusb/Makefile.linux
@@ -0,0 +1,49 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidtest-libusb libs
+
+libs: libhidapi-libusb.so
+
+CC       ?= gcc
+CFLAGS   ?= -Wall -g -fpic
+
+CXX      ?= g++
+CXXFLAGS ?= -Wall -g -fpic
+
+LDFLAGS  ?= -Wall -g
+
+COBJS_LIBUSB = hid.o
+COBJS = $(COBJS_LIBUSB)
+CPPOBJS   = ../hidtest/hidtest.o
+OBJS      = $(COBJS) $(CPPOBJS)
+LIBS_USB  = `pkg-config libusb-1.0 --libs` -lrt -lpthread
+LIBS      = $(LIBS_USB)
+INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
+
+
+# Console Test Program
+hidtest-libusb: $(COBJS_LIBUSB) $(CPPOBJS)
+	$(CXX) $(LDFLAGS) $^ $(LIBS_USB) -o $@
+
+# Shared Libs
+libhidapi-libusb.so: $(COBJS_LIBUSB)
+	$(CC) $(LDFLAGS) $(LIBS_USB) -shared -fpic -Wl,-soname,$@.0 $^ -o $@
+
+# Objects
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
+
+
+clean:
+	rm -f $(OBJS) hidtest-libusb libhidapi-libusb.so ../hidtest/hidtest.o
+
+.PHONY: clean libs
diff --git a/source/src/hidapi/libusb/hid.c b/source/src/hidapi/libusb/hid.c
new file mode 100644
index 0000000..17378ff
--- /dev/null
+++ b/source/src/hidapi/libusb/hid.c
@@ -0,0 +1,1620 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Alan Ott
+ Signal 11 Software
+
+ 8/22/2009
+ Linux Version - 6/2/2010
+ Libusb Version - 8/13/2010
+ FreeBSD Version - 11/1/2011
+
+ Copyright 2009, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        http://github.com/signal11/hidapi .
+********************************************************/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
+#endif
+
+/* C */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <locale.h>
+#include <errno.h>
+
+/* Unix */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <wchar.h>
+
+/* GNU / LibUSB */
+#include <libusb.h>
+#ifndef __ANDROID__
+#include <iconv.h>
+#endif
+
+#include "hidapi.h"
+
+#ifdef NAMESPACE
+namespace NAMESPACE
+{
+#endif
+
+#ifdef __ANDROID__
+
+/* Barrier implementation because Android/Bionic don't have pthread_barrier.
+   This implementation came from Brent Priddy and was posted on
+   StackOverflow. It is used with his permission. */
+typedef int pthread_barrierattr_t;
+typedef struct pthread_barrier {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    int count;
+    int trip_count;
+} pthread_barrier_t;
+
+static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
+{
+	if(count == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
+		return -1;
+	}
+	if(pthread_cond_init(&barrier->cond, 0) < 0) {
+		pthread_mutex_destroy(&barrier->mutex);
+		return -1;
+	}
+	barrier->trip_count = count;
+	barrier->count = 0;
+
+	return 0;
+}
+
+static int pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+	pthread_cond_destroy(&barrier->cond);
+	pthread_mutex_destroy(&barrier->mutex);
+	return 0;
+}
+
+static int pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+	pthread_mutex_lock(&barrier->mutex);
+	++(barrier->count);
+	if(barrier->count >= barrier->trip_count)
+	{
+		barrier->count = 0;
+		pthread_cond_broadcast(&barrier->cond);
+		pthread_mutex_unlock(&barrier->mutex);
+		return 1;
+	}
+	else
+	{
+		pthread_cond_wait(&barrier->cond, &(barrier->mutex));
+		pthread_mutex_unlock(&barrier->mutex);
+		return 0;
+	}
+}
+
+#endif
+
+#if defined(__cplusplus) && !defined(NAMESPACE)
+extern "C" {
+#endif
+
+#ifdef DEBUG_PRINTF
+#define LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define LOG(...) do {} while (0)
+#endif
+
+#ifndef __FreeBSD__
+#define DETACH_KERNEL_DRIVER
+#endif
+
+/* Uncomment to enable the retrieval of Usage and Usage Page in
+hid_enumerate(). Warning, on platforms different from FreeBSD
+this is very invasive as it requires the detach
+and re-attach of the kernel driver. See comments inside hid_enumerate().
+libusb HIDAPI programs are encouraged to use the interface number
+instead to differentiate between interfaces on a composite HID device. */
+/*#define INVASIVE_GET_USAGE*/
+
+/* Linked List of input reports received from the device. */
+struct input_report {
+	uint8_t *data;
+	size_t len;
+	struct input_report *next;
+};
+
+
+struct hid_device_ {
+	/* Handle to the actual device. */
+	libusb_device_handle *device_handle;
+
+	/* Endpoint information */
+	int input_endpoint;
+	int output_endpoint;
+	int input_ep_max_packet_size;
+
+	/* The interface number of the HID */
+	int interface;
+
+	/* Indexes of Strings */
+	int manufacturer_index;
+	int product_index;
+	int serial_index;
+
+	/* Whether blocking reads are used */
+	int blocking; /* boolean */
+
+	/* Read thread objects */
+	pthread_t thread;
+	pthread_mutex_t mutex; /* Protects input_reports */
+	pthread_cond_t condition;
+	pthread_barrier_t barrier; /* Ensures correct startup sequence */
+	int shutdown_thread;
+	int cancelled;
+	struct libusb_transfer *transfer;
+
+	/* List of received input reports. */
+	struct input_report *input_reports;
+};
+
+static libusb_context *usb_context = NULL;
+
+uint16_t get_usb_code_for_current_locale(void);
+static int return_data(hid_device *dev, unsigned char *data, size_t length);
+
+static hid_device *new_hid_device(void)
+{
+	hid_device *dev = (hid_device *)calloc(1, sizeof(hid_device));
+	dev->blocking = 1;
+
+	pthread_mutex_init(&dev->mutex, NULL);
+	pthread_cond_init(&dev->condition, NULL);
+	pthread_barrier_init(&dev->barrier, NULL, 2);
+
+	return dev;
+}
+
+static void free_hid_device(hid_device *dev)
+{
+	/* Clean up the thread objects */
+	pthread_barrier_destroy(&dev->barrier);
+	pthread_cond_destroy(&dev->condition);
+	pthread_mutex_destroy(&dev->mutex);
+
+	/* Free the device itself */
+	free(dev);
+}
+
+#if 0
+/*TODO: Implement this funciton on hidapi/libusb.. */
+static void register_error(hid_device *device, const char *op)
+{
+
+}
+#endif
+
+#ifdef INVASIVE_GET_USAGE
+/* Get bytes from a HID Report Descriptor.
+   Only call with a num_bytes of 0, 1, 2, or 4. */
+static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur)
+{
+	/* Return if there aren't enough bytes. */
+	if (cur + num_bytes >= len)
+		return 0;
+
+	if (num_bytes == 0)
+		return 0;
+	else if (num_bytes == 1) {
+		return rpt[cur+1];
+	}
+	else if (num_bytes == 2) {
+		return (rpt[cur+2] * 256 + rpt[cur+1]);
+	}
+	else if (num_bytes == 4) {
+		return (rpt[cur+4] * 0x01000000 +
+		        rpt[cur+3] * 0x00010000 +
+		        rpt[cur+2] * 0x00000100 +
+		        rpt[cur+1] * 0x00000001);
+	}
+	else
+		return 0;
+}
+
+/* Retrieves the device's Usage Page and Usage from the report
+   descriptor. The algorithm is simple, as it just returns the first
+   Usage and Usage Page that it finds in the descriptor.
+   The return value is 0 on success and -1 on failure. */
+static int get_usage(uint8_t *report_descriptor, size_t size,
+                     unsigned short *usage_page, unsigned short *usage)
+{
+	unsigned int i = 0;
+	int size_code;
+	int data_len, key_size;
+	int usage_found = 0, usage_page_found = 0;
+
+	while (i < size) {
+		int key = report_descriptor[i];
+		int key_cmd = key & 0xfc;
+
+		//printf("key: %02hhx\n", key);
+
+		if ((key & 0xf0) == 0xf0) {
+			/* This is a Long Item. The next byte contains the
+			   length of the data section (value) for this key.
+			   See the HID specification, version 1.11, section
+			   6.2.2.3, titled "Long Items." */
+			if (i+1 < size)
+				data_len = report_descriptor[i+1];
+			else
+				data_len = 0; /* malformed report */
+			key_size = 3;
+		}
+		else {
+			/* This is a Short Item. The bottom two bits of the
+			   key contain the size code for the data section
+			   (value) for this key.  Refer to the HID
+			   specification, version 1.11, section 6.2.2.2,
+			   titled "Short Items." */
+			size_code = key & 0x3;
+			switch (size_code) {
+			case 0:
+			case 1:
+			case 2:
+				data_len = size_code;
+				break;
+			case 3:
+				data_len = 4;
+				break;
+			default:
+				/* Can't ever happen since size_code is & 0x3 */
+				data_len = 0;
+				break;
+			};
+			key_size = 1;
+		}
+
+		if (key_cmd == 0x4) {
+			*usage_page  = get_bytes(report_descriptor, size, data_len, i);
+			usage_page_found = 1;
+			//printf("Usage Page: %x\n", (uint32_t)*usage_page);
+		}
+		if (key_cmd == 0x8) {
+			*usage = get_bytes(report_descriptor, size, data_len, i);
+			usage_found = 1;
+			//printf("Usage: %x\n", (uint32_t)*usage);
+		}
+
+		if (usage_page_found && usage_found)
+			return 0; /* success */
+
+		/* Skip over this key and it's associated data */
+		i += data_len + key_size;
+	}
+
+	return -1; /* failure */
+}
+#endif /* INVASIVE_GET_USAGE */
+
+#if defined(__FreeBSD__) && __FreeBSD__ < 10
+/* The libusb version included in FreeBSD < 10 doesn't have this function. In
+   mainline libusb, it's inlined in libusb.h. This function will bear a striking
+   resemblance to that one, because there's about one way to code it.
+
+   Note that the data parameter is Unicode in UTF-16LE encoding.
+   Return value is the number of bytes in data, or LIBUSB_ERROR_*.
+ */
+static inline int libusb_get_string_descriptor(libusb_device_handle *dev,
+	uint8_t descriptor_index, uint16_t lang_id,
+	unsigned char *data, int length)
+{
+	return libusb_control_transfer(dev,
+		LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */
+		LIBUSB_REQUEST_GET_DESCRIPTOR,
+		(LIBUSB_DT_STRING << 8) | descriptor_index,
+		lang_id, data, (uint16_t) length, 1000);
+}
+
+#endif
+
+
+/* Get the first language the device says it reports. This comes from
+   USB string #0. */
+static uint16_t get_first_language(libusb_device_handle *dev)
+{
+	uint16_t buf[32];
+	int len;
+
+	/* Get the string from libusb. */
+	len = libusb_get_string_descriptor(dev,
+			0x0, /* String ID */
+			0x0, /* Language */
+			(unsigned char*)buf,
+			sizeof(buf));
+	if (len < 4)
+		return 0x0;
+
+	return buf[1]; /* First two bytes are len and descriptor type. */
+}
+
+static int is_language_supported(libusb_device_handle *dev, uint16_t lang)
+{
+	uint16_t buf[32];
+	int len;
+	int i;
+
+	/* Get the string from libusb. */
+	len = libusb_get_string_descriptor(dev,
+			0x0, /* String ID */
+			0x0, /* Language */
+			(unsigned char*)buf,
+			sizeof(buf));
+	if (len < 4)
+		return 0x0;
+
+
+	len /= 2; /* language IDs are two-bytes each. */
+	/* Start at index 1 because there are two bytes of protocol data. */
+	for (i = 1; i < len; i++) {
+		if (buf[i] == lang)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+/* This function returns a newly allocated wide string containing the USB
+   device string numbered by the index. The returned string must be freed
+   by using free(). */
+static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx)
+{
+	char buf[512];
+	int len;
+	wchar_t *str = NULL;
+
+#ifndef __ANDROID__ /* we don't use iconv on Android */
+	wchar_t wbuf[256];
+	/* iconv variables */
+	iconv_t ic;
+	size_t inbytes;
+	size_t outbytes;
+	size_t res;
+#ifdef __FreeBSD__
+	const char *inptr;
+#else
+	char *inptr;
+#endif
+	char *outptr;
+#endif
+
+	/* Determine which language to use. */
+	uint16_t lang;
+	lang = get_usb_code_for_current_locale();
+	if (!is_language_supported(dev, lang))
+		lang = get_first_language(dev);
+
+	/* Get the string from libusb. */
+	len = libusb_get_string_descriptor(dev,
+			idx,
+			lang,
+			(unsigned char*)buf,
+			sizeof(buf));
+	if (len < 0)
+		return NULL;
+
+#ifdef __ANDROID__
+
+	/* Bionic does not have iconv support nor wcsdup() function, so it
+	   has to be done manually.  The following code will only work for
+	   code points that can be represented as a single UTF-16 character,
+	   and will incorrectly convert any code points which require more
+	   than one UTF-16 character.
+
+	   Skip over the first character (2-bytes).  */
+	len -= 2;
+	str = malloc((len / 2 + 1) * sizeof(wchar_t));
+	int i;
+	for (i = 0; i < len / 2; i++) {
+		str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8);
+	}
+	str[len / 2] = 0x00000000;
+
+#else
+
+	/* buf does not need to be explicitly NULL-terminated because
+	   it is only passed into iconv() which does not need it. */
+
+	/* Initialize iconv. */
+	ic = iconv_open("WCHAR_T", "UTF-16LE");
+	if (ic == (iconv_t)-1) {
+		LOG("iconv_open() failed\n");
+		return NULL;
+	}
+
+	/* Convert to native wchar_t (UTF-32 on glibc/BSD systems).
+	   Skip the first character (2-bytes). */
+	inptr = buf+2;
+	inbytes = len-2;
+	outptr = (char*) wbuf;
+	outbytes = sizeof(wbuf);
+	res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes);
+	if (res == (size_t)-1) {
+		LOG("iconv() failed\n");
+		goto err;
+	}
+
+	/* Write the terminating NULL. */
+	wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000;
+	if (outbytes >= sizeof(wbuf[0]))
+		*((wchar_t*)outptr) = 0x00000000;
+
+	/* Allocate and copy the string. */
+	str = wcsdup(wbuf);
+
+err:
+	iconv_close(ic);
+
+#endif
+
+	return str;
+}
+
+static char *make_path(libusb_device *dev, int interface_number)
+{
+	char str[64];
+	snprintf(str, sizeof(str), "%04x:%04x:%02x",
+		libusb_get_bus_number(dev),
+		libusb_get_device_address(dev),
+		interface_number);
+	str[sizeof(str)-1] = '\0';
+
+	return strdup(str);
+}
+
+
+int HID_API_EXPORT hid_init(void)
+{
+	if (!usb_context) {
+		const char *locale;
+
+		/* Init Libusb */
+		if (libusb_init(&usb_context))
+			return -1;
+
+		/* Set the locale if it's not set. */
+		locale = setlocale(LC_CTYPE, NULL);
+		if (!locale)
+			setlocale(LC_CTYPE, "");
+	}
+
+	return 0;
+}
+
+int HID_API_EXPORT hid_exit(void)
+{
+	if (usb_context) {
+		libusb_exit(usb_context);
+		usb_context = NULL;
+	}
+
+	return 0;
+}
+
+static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+{
+	static const int XB360_IFACE_SUBCLASS = 93;
+	static const int XB360_IFACE_PROTOCOL = 1; /* Wired only */
+	static const int SUPPORTED_VENDORS[] = {
+		0x0079, /* GPD Win 2 */
+		0x044f, /* Thrustmaster */
+		0x045e, /* Microsoft */
+		0x046d, /* Logitech */
+		0x056e, /* Elecom */
+		0x06a3, /* Saitek */
+		0x0738, /* Mad Catz */
+		0x07ff, /* Mad Catz */
+		0x0e6f, /* Unknown */
+		0x0f0d, /* Hori */
+		0x11c9, /* Nacon */
+		0x12ab, /* Unknown */
+		0x1430, /* RedOctane */
+		0x146b, /* BigBen */
+		0x1532, /* Razer Sabertooth */
+		0x15e4, /* Numark */
+		0x162e, /* Joytech */
+		0x1689, /* Razer Onza */
+		0x1bad, /* Harmonix */
+		0x24c6, /* PowerA */
+	};
+
+	if (intf_desc->bInterfaceNumber == 0 &&
+	    intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
+	    intf_desc->bInterfaceSubClass == XB360_IFACE_SUBCLASS &&
+	    intf_desc->bInterfaceProtocol == XB360_IFACE_PROTOCOL) {
+		int i;
+		for (i = 0; i < sizeof(SUPPORTED_VENDORS)/sizeof(SUPPORTED_VENDORS[0]); ++i) {
+			if (vendor_id == SUPPORTED_VENDORS[i]) {
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+{
+        static const int XB1_IFACE_SUBCLASS = 71;
+        static const int XB1_IFACE_PROTOCOL = 208;
+        static const int SUPPORTED_VENDORS[] = {
+            0x045e, /* Microsoft */
+            0x0738, /* Mad Catz */
+            0x0e6f, /* Unknown */
+            0x0f0d, /* Hori */
+            0x1532, /* Razer Wildcat */
+            0x24c6, /* PowerA */
+        };
+
+	if (intf_desc->bInterfaceNumber == 0 &&
+	    intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
+	    intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS &&
+	    intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) {
+		int i;
+		for (i = 0; i < sizeof(SUPPORTED_VENDORS)/sizeof(SUPPORTED_VENDORS[0]); ++i) {
+			if (vendor_id == SUPPORTED_VENDORS[i]) {
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int should_enumerate_interface(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+{
+	if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID)
+		return 1;
+
+	/* Also enumerate Xbox 360 controllers */
+	if (is_xbox360(vendor_id, intf_desc))
+	{
+		/* hid_write() to Xbox 360 controllers doesn't seem to work on Linux:
+		   - xpad 1-2:1.0: xpad_try_sending_next_out_packet - usb_submit_urb failed with result -2
+		   Xbox 360 controller support is good on Linux anyway, so we'll ignore this for now.
+		return 1;
+		*/
+	}
+
+	/* Also enumerate Xbox One controllers */
+	if (is_xboxone(vendor_id, intf_desc))
+		return 1;
+
+	return 0;
+}
+
+struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+	libusb_device **devs;
+	libusb_device *dev;
+	libusb_device_handle *handle;
+	ssize_t num_devs;
+	int i = 0;
+
+	struct hid_device_info *root = NULL; /* return object */
+	struct hid_device_info *cur_dev = NULL;
+
+	if(hid_init() < 0)
+		return NULL;
+
+	num_devs = libusb_get_device_list(usb_context, &devs);
+	if (num_devs < 0)
+		return NULL;
+	while ((dev = devs[i++]) != NULL) {
+		struct libusb_device_descriptor desc;
+		struct libusb_config_descriptor *conf_desc = NULL;
+		int j, k;
+		int interface_num = 0;
+
+		int res = libusb_get_device_descriptor(dev, &desc);
+		unsigned short dev_vid = desc.idVendor;
+		unsigned short dev_pid = desc.idProduct;
+
+		res = libusb_get_active_config_descriptor(dev, &conf_desc);
+		if (res < 0)
+			libusb_get_config_descriptor(dev, 0, &conf_desc);
+		if (conf_desc) {
+			for (j = 0; j < conf_desc->bNumInterfaces; j++) {
+				const struct libusb_interface *intf = &conf_desc->interface[j];
+				for (k = 0; k < intf->num_altsetting; k++) {
+					const struct libusb_interface_descriptor *intf_desc;
+					intf_desc = &intf->altsetting[k];
+					if (should_enumerate_interface(dev_vid, intf_desc)) {
+						interface_num = intf_desc->bInterfaceNumber;
+
+						/* Check the VID/PID against the arguments */
+						if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
+						    (product_id == 0x0 || product_id == dev_pid)) {
+							struct hid_device_info *tmp;
+
+							/* VID/PID match. Create the record. */
+							tmp = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
+							if (cur_dev) {
+								cur_dev->next = tmp;
+							}
+							else {
+								root = tmp;
+							}
+							cur_dev = tmp;
+
+							/* Fill out the record */
+							cur_dev->next = NULL;
+							cur_dev->path = make_path(dev, interface_num);
+
+							res = libusb_open(dev, &handle);
+
+							if (res >= 0) {
+								/* Serial Number */
+								if (desc.iSerialNumber > 0)
+									cur_dev->serial_number =
+										get_usb_string(handle, desc.iSerialNumber);
+
+								/* Manufacturer and Product strings */
+								if (desc.iManufacturer > 0)
+									cur_dev->manufacturer_string =
+										get_usb_string(handle, desc.iManufacturer);
+								if (desc.iProduct > 0)
+									cur_dev->product_string =
+										get_usb_string(handle, desc.iProduct);
+
+#ifdef INVASIVE_GET_USAGE
+{
+							/*
+							This section is removed because it is too
+							invasive on the system. Getting a Usage Page
+							and Usage requires parsing the HID Report
+							descriptor. Getting a HID Report descriptor
+							involves claiming the interface. Claiming the
+							interface involves detaching the kernel driver.
+							Detaching the kernel driver is hard on the system
+							because it will unclaim interfaces (if another
+							app has them claimed) and the re-attachment of
+							the driver will sometimes change /dev entry names.
+							It is for these reasons that this section is
+							#if 0. For composite devices, use the interface
+							field in the hid_device_info struct to distinguish
+							between interfaces. */
+								unsigned char data[256];
+#ifdef DETACH_KERNEL_DRIVER
+								int detached = 0;
+								/* Usage Page and Usage */
+								res = libusb_kernel_driver_active(handle, interface_num);
+								if (res == 1) {
+									res = libusb_detach_kernel_driver(handle, interface_num);
+									if (res < 0)
+										LOG("Couldn't detach kernel driver, even though a kernel driver was attached.");
+									else
+										detached = 1;
+								}
+#endif
+								res = libusb_claim_interface(handle, interface_num);
+								if (res >= 0) {
+									/* Get the HID Report Descriptor. */
+									res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000);
+									if (res >= 0) {
+										unsigned short page=0, usage=0;
+										/* Parse the usage and usage page
+										   out of the report descriptor. */
+										get_usage(data, res,  &page, &usage);
+										cur_dev->usage_page = page;
+										cur_dev->usage = usage;
+									}
+									else
+										LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res);
+
+									/* Release the interface */
+									res = libusb_release_interface(handle, interface_num);
+									if (res < 0)
+										LOG("Can't release the interface.\n");
+								}
+								else
+									LOG("Can't claim interface %d\n", res);
+#ifdef DETACH_KERNEL_DRIVER
+								/* Re-attach kernel driver if necessary. */
+								if (detached) {
+									res = libusb_attach_kernel_driver(handle, interface_num);
+									if (res < 0)
+										LOG("Couldn't re-attach kernel driver.\n");
+								}
+#endif
+}
+#endif /* INVASIVE_GET_USAGE */
+
+								libusb_close(handle);
+							}
+							/* VID/PID */
+							cur_dev->vendor_id = dev_vid;
+							cur_dev->product_id = dev_pid;
+
+							/* Release Number */
+							cur_dev->release_number = desc.bcdDevice;
+
+							/* Interface Number */
+							cur_dev->interface_number = interface_num;
+						}
+					}
+				} /* altsettings */
+			} /* interfaces */
+			libusb_free_config_descriptor(conf_desc);
+		}
+	}
+
+	libusb_free_device_list(devs, 1);
+
+	return root;
+}
+
+void  HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
+{
+	struct hid_device_info *d = devs;
+	while (d) {
+		struct hid_device_info *next = d->next;
+		free(d->path);
+		free(d->serial_number);
+		free(d->manufacturer_string);
+		free(d->product_string);
+		free(d);
+		d = next;
+	}
+}
+
+hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
+{
+	struct hid_device_info *devs, *cur_dev;
+	const char *path_to_open = NULL;
+	hid_device *handle = NULL;
+
+	devs = hid_enumerate(vendor_id, product_id);
+	cur_dev = devs;
+	while (cur_dev) {
+		if (cur_dev->vendor_id == vendor_id &&
+		    cur_dev->product_id == product_id) {
+			if (serial_number) {
+				if (cur_dev->serial_number &&
+				    wcscmp(serial_number, cur_dev->serial_number) == 0) {
+					path_to_open = cur_dev->path;
+					break;
+				}
+			}
+			else {
+				path_to_open = cur_dev->path;
+				break;
+			}
+		}
+		cur_dev = cur_dev->next;
+	}
+
+	if (path_to_open) {
+		/* Open the device */
+		handle = hid_open_path(path_to_open, 0);
+	}
+
+	hid_free_enumeration(devs);
+
+	return handle;
+}
+
+static void read_callback(struct libusb_transfer *transfer)
+{
+	hid_device *dev = (hid_device *)transfer->user_data;
+	int res;
+
+	if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+
+		struct input_report *rpt = (struct input_report *)malloc(sizeof(*rpt));
+		rpt->data = (uint8_t *)malloc(transfer->actual_length);
+		memcpy(rpt->data, transfer->buffer, transfer->actual_length);
+		rpt->len = transfer->actual_length;
+		rpt->next = NULL;
+
+		pthread_mutex_lock(&dev->mutex);
+
+		/* Attach the new report object to the end of the list. */
+		if (dev->input_reports == NULL) {
+			/* The list is empty. Put it at the root. */
+			dev->input_reports = rpt;
+			pthread_cond_signal(&dev->condition);
+		}
+		else {
+			/* Find the end of the list and attach. */
+			struct input_report *cur = dev->input_reports;
+			int num_queued = 0;
+			while (cur->next != NULL) {
+				cur = cur->next;
+				num_queued++;
+			}
+			cur->next = rpt;
+
+			/* Pop one off if we've reached 30 in the queue. This
+			   way we don't grow forever if the user never reads
+			   anything from the device. */
+			if (num_queued > 30) {
+				return_data(dev, NULL, 0);
+			}
+		}
+		pthread_mutex_unlock(&dev->mutex);
+	}
+	else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+		dev->shutdown_thread = 1;
+		dev->cancelled = 1;
+		return;
+	}
+	else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
+		dev->shutdown_thread = 1;
+		dev->cancelled = 1;
+		return;
+	}
+	else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
+		//LOG("Timeout (normal)\n");
+	}
+	else {
+		LOG("Unknown transfer code: %d\n", transfer->status);
+	}
+
+	/* Re-submit the transfer object. */
+	res = libusb_submit_transfer(transfer);
+	if (res != 0) {
+		LOG("Unable to submit URB. libusb error code: %d\n", res);
+		dev->shutdown_thread = 1;
+		dev->cancelled = 1;
+	}
+}
+
+
+static void *read_thread(void *param)
+{
+	hid_device *dev = (hid_device *)param;
+	unsigned char *buf;
+	const size_t length = dev->input_ep_max_packet_size;
+
+	/* Set up the transfer object. */
+	buf = (unsigned char *)malloc(length);
+	dev->transfer = libusb_alloc_transfer(0);
+	libusb_fill_interrupt_transfer(dev->transfer,
+		dev->device_handle,
+		dev->input_endpoint,
+		buf,
+		length,
+		read_callback,
+		dev,
+		5000/*timeout*/);
+
+	/* Make the first submission. Further submissions are made
+	   from inside read_callback() */
+	libusb_submit_transfer(dev->transfer);
+
+	/* Notify the main thread that the read thread is up and running. */
+	pthread_barrier_wait(&dev->barrier);
+
+	/* Handle all the events. */
+	while (!dev->shutdown_thread) {
+		int res;
+		res = libusb_handle_events(usb_context);
+		if (res < 0) {
+			/* There was an error. */
+			LOG("read_thread(): libusb reports error # %d\n", res);
+
+			/* Break out of this loop only on fatal error.*/
+			if (res != LIBUSB_ERROR_BUSY &&
+			    res != LIBUSB_ERROR_TIMEOUT &&
+			    res != LIBUSB_ERROR_OVERFLOW &&
+			    res != LIBUSB_ERROR_INTERRUPTED) {
+				break;
+			}
+		}
+	}
+
+	/* Cancel any transfer that may be pending. This call will fail
+	   if no transfers are pending, but that's OK. */
+	libusb_cancel_transfer(dev->transfer);
+
+	while (!dev->cancelled)
+		libusb_handle_events_completed(usb_context, &dev->cancelled);
+
+	/* Now that the read thread is stopping, Wake any threads which are
+	   waiting on data (in hid_read_timeout()). Do this under a mutex to
+	   make sure that a thread which is about to go to sleep waiting on
+	   the condition actually will go to sleep before the condition is
+	   signaled. */
+	pthread_mutex_lock(&dev->mutex);
+	pthread_cond_broadcast(&dev->condition);
+	pthread_mutex_unlock(&dev->mutex);
+
+	/* The dev->transfer->buffer and dev->transfer objects are cleaned up
+	   in hid_close(). They are not cleaned up here because this thread
+	   could end either due to a disconnect or due to a user
+	   call to hid_close(). In both cases the objects can be safely
+	   cleaned up after the call to pthread_join() (in hid_close()), but
+	   since hid_close() calls libusb_cancel_transfer(), on these objects,
+	   they can not be cleaned up here. */
+
+	return NULL;
+}
+
+
+hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
+{
+	hid_device *dev = NULL;
+
+	libusb_device **devs;
+	libusb_device *usb_dev;
+	int res;
+	int d = 0;
+	int good_open = 0;
+
+	if(hid_init() < 0)
+		return NULL;
+
+	dev = new_hid_device();
+
+	libusb_get_device_list(usb_context, &devs);
+	while ((usb_dev = devs[d++]) != NULL) {
+		struct libusb_device_descriptor desc;
+		struct libusb_config_descriptor *conf_desc = NULL;
+		int i,j,k;
+		libusb_get_device_descriptor(usb_dev, &desc);
+
+		if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0)
+			continue;
+		for (j = 0; j < conf_desc->bNumInterfaces; j++) {
+			const struct libusb_interface *intf = &conf_desc->interface[j];
+			for (k = 0; k < intf->num_altsetting; k++) {
+				const struct libusb_interface_descriptor *intf_desc;
+				intf_desc = &intf->altsetting[k];
+				if (should_enumerate_interface(desc.idVendor, intf_desc)) {
+					char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber);
+					if (!strcmp(dev_path, path)) {
+						/* Matched Paths. Open this device */
+
+						/* OPEN HERE */
+						res = libusb_open(usb_dev, &dev->device_handle);
+						if (res < 0) {
+							LOG("can't open device\n");
+							free(dev_path);
+							break;
+						}
+						good_open = 1;
+#ifdef DETACH_KERNEL_DRIVER
+						/* Detach the kernel driver, but only if the
+						   device is managed by the kernel */
+						if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) {
+							res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
+							if (res < 0) {
+								libusb_close(dev->device_handle);
+								LOG("Unable to detach Kernel Driver\n");
+								free(dev_path);
+								good_open = 0;
+								break;
+							}
+						}
+#endif
+						res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
+						if (res < 0) {
+							LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
+							free(dev_path);
+							libusb_close(dev->device_handle);
+							good_open = 0;
+							break;
+						}
+
+						/* Store off the string descriptor indexes */
+						dev->manufacturer_index = desc.iManufacturer;
+						dev->product_index      = desc.iProduct;
+						dev->serial_index       = desc.iSerialNumber;
+
+						/* Store off the interface number */
+						dev->interface = intf_desc->bInterfaceNumber;
+
+						/* Find the INPUT and OUTPUT endpoints. An
+						   OUTPUT endpoint is not required. */
+						for (i = 0; i < intf_desc->bNumEndpoints; i++) {
+							const struct libusb_endpoint_descriptor *ep
+								= &intf_desc->endpoint[i];
+
+							/* Determine the type and direction of this
+							   endpoint. */
+							int is_interrupt =
+								(ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
+							      == LIBUSB_TRANSFER_TYPE_INTERRUPT;
+							int is_output =
+								(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
+							      == LIBUSB_ENDPOINT_OUT;
+							int is_input =
+								(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
+							      == LIBUSB_ENDPOINT_IN;
+
+							/* Decide whether to use it for input or output. */
+							if (dev->input_endpoint == 0 &&
+							    is_interrupt && is_input) {
+								/* Use this endpoint for INPUT */
+								dev->input_endpoint = ep->bEndpointAddress;
+								dev->input_ep_max_packet_size = ep->wMaxPacketSize;
+							}
+							if (dev->output_endpoint == 0 &&
+							    is_interrupt && is_output) {
+								/* Use this endpoint for OUTPUT */
+								dev->output_endpoint = ep->bEndpointAddress;
+							}
+						}
+
+						pthread_create(&dev->thread, NULL, read_thread, dev);
+
+						/* Wait here for the read thread to be initialized. */
+						pthread_barrier_wait(&dev->barrier);
+
+					}
+					free(dev_path);
+				}
+			}
+		}
+		libusb_free_config_descriptor(conf_desc);
+
+	}
+
+	libusb_free_device_list(devs, 1);
+
+	/* If we have a good handle, return it. */
+	if (good_open) {
+		return dev;
+	}
+	else {
+		/* Unable to open any devices. */
+		free_hid_device(dev);
+		return NULL;
+	}
+}
+
+
+int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
+{
+	int res;
+	int report_number = data[0];
+	int skipped_report_id = 0;
+
+	if (report_number == 0x0) {
+		data++;
+		length--;
+		skipped_report_id = 1;
+	}
+
+
+	if (dev->output_endpoint <= 0) {
+		/* No interrupt out endpoint. Use the Control Endpoint */
+		res = libusb_control_transfer(dev->device_handle,
+			LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
+			0x09/*HID Set_Report*/,
+			(2/*HID output*/ << 8) | report_number,
+			dev->interface,
+			(unsigned char *)data, length,
+			1000/*timeout millis*/);
+
+		if (res < 0)
+			return -1;
+
+		if (skipped_report_id)
+			length++;
+
+		return length;
+	}
+	else {
+		/* Use the interrupt out endpoint */
+		int actual_length;
+		res = libusb_interrupt_transfer(dev->device_handle,
+			dev->output_endpoint,
+			(unsigned char*)data,
+			length,
+			&actual_length, 1000);
+
+		if (res < 0)
+			return -1;
+
+		if (skipped_report_id)
+			actual_length++;
+
+		return actual_length;
+	}
+}
+
+/* Helper function, to simplify hid_read().
+   This should be called with dev->mutex locked. */
+static int return_data(hid_device *dev, unsigned char *data, size_t length)
+{
+	/* Copy the data out of the linked list item (rpt) into the
+	   return buffer (data), and delete the liked list item. */
+	struct input_report *rpt = dev->input_reports;
+	size_t len = (length < rpt->len)? length: rpt->len;
+	if (data && len > 0)
+		memcpy(data, rpt->data, len);
+	dev->input_reports = rpt->next;
+	free(rpt->data);
+	free(rpt);
+	return len;
+}
+
+static void cleanup_mutex(void *param)
+{
+	hid_device *dev = (hid_device *)param;
+	pthread_mutex_unlock(&dev->mutex);
+}
+
+
+int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
+{
+	int bytes_read = -1;
+
+#if 0
+	int transferred;
+	int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000);
+	LOG("transferred: %d\n", transferred);
+	return transferred;
+#endif
+
+	pthread_mutex_lock(&dev->mutex);
+	pthread_cleanup_push(&cleanup_mutex, dev);
+
+	/* There's an input report queued up. Return it. */
+	if (dev->input_reports) {
+		/* Return the first one */
+		bytes_read = return_data(dev, data, length);
+		goto ret;
+	}
+
+	if (dev->shutdown_thread) {
+		/* This means the device has been disconnected.
+		   An error code of -1 should be returned. */
+		bytes_read = -1;
+		goto ret;
+	}
+
+	if (milliseconds == -1) {
+		/* Blocking */
+		while (!dev->input_reports && !dev->shutdown_thread) {
+			pthread_cond_wait(&dev->condition, &dev->mutex);
+		}
+		if (dev->input_reports) {
+			bytes_read = return_data(dev, data, length);
+		}
+	}
+	else if (milliseconds > 0) {
+		/* Non-blocking, but called with timeout. */
+		int res;
+		struct timespec ts;
+		clock_gettime(CLOCK_REALTIME, &ts);
+		ts.tv_sec += milliseconds / 1000;
+		ts.tv_nsec += (milliseconds % 1000) * 1000000;
+		if (ts.tv_nsec >= 1000000000L) {
+			ts.tv_sec++;
+			ts.tv_nsec -= 1000000000L;
+		}
+
+		while (!dev->input_reports && !dev->shutdown_thread) {
+			res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts);
+			if (res == 0) {
+				if (dev->input_reports) {
+					bytes_read = return_data(dev, data, length);
+					break;
+				}
+
+				/* If we're here, there was a spurious wake up
+				   or the read thread was shutdown. Run the
+				   loop again (ie: don't break). */
+			}
+			else if (res == ETIMEDOUT) {
+				/* Timed out. */
+				bytes_read = 0;
+				break;
+			}
+			else {
+				/* Error. */
+				bytes_read = -1;
+				break;
+			}
+		}
+	}
+	else {
+		/* Purely non-blocking */
+		bytes_read = 0;
+	}
+
+ret:
+	pthread_mutex_unlock(&dev->mutex);
+	pthread_cleanup_pop(0);
+
+	return bytes_read;
+}
+
+int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
+{
+	return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0);
+}
+
+int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
+{
+	dev->blocking = !nonblock;
+
+	return 0;
+}
+
+
+int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
+{
+	int res = -1;
+	int skipped_report_id = 0;
+	int report_number = data[0];
+
+	if (report_number == 0x0) {
+		data++;
+		length--;
+		skipped_report_id = 1;
+	}
+
+	res = libusb_control_transfer(dev->device_handle,
+		LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
+		0x09/*HID set_report*/,
+		(3/*HID feature*/ << 8) | report_number,
+		dev->interface,
+		(unsigned char *)data, length,
+		1000/*timeout millis*/);
+
+	if (res < 0)
+		return -1;
+
+	/* Account for the report ID */
+	if (skipped_report_id)
+		length++;
+
+	return length;
+}
+
+int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+{
+	int res = -1;
+	int skipped_report_id = 0;
+	int report_number = data[0];
+
+	if (report_number == 0x0) {
+		/* Offset the return buffer by 1, so that the report ID
+		   will remain in byte 0. */
+		data++;
+		length--;
+		skipped_report_id = 1;
+	}
+	res = libusb_control_transfer(dev->device_handle,
+		LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN,
+		0x01/*HID get_report*/,
+		(3/*HID feature*/ << 8) | report_number,
+		dev->interface,
+		(unsigned char *)data, length,
+		1000/*timeout millis*/);
+
+	if (res < 0)
+		return -1;
+
+	if (skipped_report_id)
+		res++;
+
+	return res;
+}
+
+
+void HID_API_EXPORT hid_close(hid_device *dev)
+{
+	if (!dev)
+		return;
+
+	/* Cause read_thread() to stop. */
+	dev->shutdown_thread = 1;
+	libusb_cancel_transfer(dev->transfer);
+
+	/* Wait for read_thread() to end. */
+	pthread_join(dev->thread, NULL);
+
+	/* Clean up the Transfer objects allocated in read_thread(). */
+	free(dev->transfer->buffer);
+	libusb_free_transfer(dev->transfer);
+
+	/* release the interface */
+	libusb_release_interface(dev->device_handle, dev->interface);
+
+	/* Close the handle */
+	libusb_close(dev->device_handle);
+
+	/* Clear out the queue of received reports. */
+	pthread_mutex_lock(&dev->mutex);
+	while (dev->input_reports) {
+		return_data(dev, NULL, 0);
+	}
+	pthread_mutex_unlock(&dev->mutex);
+
+	free_hid_device(dev);
+}
+
+
+int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return hid_get_indexed_string(dev, dev->product_index, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return hid_get_indexed_string(dev, dev->serial_index, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
+{
+	wchar_t *str;
+
+	str = get_usb_string(dev->device_handle, string_index);
+	if (str) {
+		wcsncpy(string, str, maxlen);
+		string[maxlen-1] = L'\0';
+		free(str);
+		return 0;
+	}
+	else
+		return -1;
+}
+
+
+HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
+{
+	return NULL;
+}
+
+
+struct lang_map_entry {
+	const char *name;
+	const char *string_code;
+	uint16_t usb_code;
+};
+
+#define LANG(name,code,usb_code) { name, code, usb_code }
+static struct lang_map_entry lang_map[] = {
+	LANG("Afrikaans", "af", 0x0436),
+	LANG("Albanian", "sq", 0x041C),
+	LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801),
+	LANG("Arabic - Bahrain", "ar_bh", 0x3C01),
+	LANG("Arabic - Algeria", "ar_dz", 0x1401),
+	LANG("Arabic - Egypt", "ar_eg", 0x0C01),
+	LANG("Arabic - Iraq", "ar_iq", 0x0801),
+	LANG("Arabic - Jordan", "ar_jo", 0x2C01),
+	LANG("Arabic - Kuwait", "ar_kw", 0x3401),
+	LANG("Arabic - Lebanon", "ar_lb", 0x3001),
+	LANG("Arabic - Libya", "ar_ly", 0x1001),
+	LANG("Arabic - Morocco", "ar_ma", 0x1801),
+	LANG("Arabic - Oman", "ar_om", 0x2001),
+	LANG("Arabic - Qatar", "ar_qa", 0x4001),
+	LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401),
+	LANG("Arabic - Syria", "ar_sy", 0x2801),
+	LANG("Arabic - Tunisia", "ar_tn", 0x1C01),
+	LANG("Arabic - Yemen", "ar_ye", 0x2401),
+	LANG("Armenian", "hy", 0x042B),
+	LANG("Azeri - Latin", "az_az", 0x042C),
+	LANG("Azeri - Cyrillic", "az_az", 0x082C),
+	LANG("Basque", "eu", 0x042D),
+	LANG("Belarusian", "be", 0x0423),
+	LANG("Bulgarian", "bg", 0x0402),
+	LANG("Catalan", "ca", 0x0403),
+	LANG("Chinese - China", "zh_cn", 0x0804),
+	LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04),
+	LANG("Chinese - Macau SAR", "zh_mo", 0x1404),
+	LANG("Chinese - Singapore", "zh_sg", 0x1004),
+	LANG("Chinese - Taiwan", "zh_tw", 0x0404),
+	LANG("Croatian", "hr", 0x041A),
+	LANG("Czech", "cs", 0x0405),
+	LANG("Danish", "da", 0x0406),
+	LANG("Dutch - Netherlands", "nl_nl", 0x0413),
+	LANG("Dutch - Belgium", "nl_be", 0x0813),
+	LANG("English - Australia", "en_au", 0x0C09),
+	LANG("English - Belize", "en_bz", 0x2809),
+	LANG("English - Canada", "en_ca", 0x1009),
+	LANG("English - Caribbean", "en_cb", 0x2409),
+	LANG("English - Ireland", "en_ie", 0x1809),
+	LANG("English - Jamaica", "en_jm", 0x2009),
+	LANG("English - New Zealand", "en_nz", 0x1409),
+	LANG("English - Phillippines", "en_ph", 0x3409),
+	LANG("English - Southern Africa", "en_za", 0x1C09),
+	LANG("English - Trinidad", "en_tt", 0x2C09),
+	LANG("English - Great Britain", "en_gb", 0x0809),
+	LANG("English - United States", "en_us", 0x0409),
+	LANG("Estonian", "et", 0x0425),
+	LANG("Farsi", "fa", 0x0429),
+	LANG("Finnish", "fi", 0x040B),
+	LANG("Faroese", "fo", 0x0438),
+	LANG("French - France", "fr_fr", 0x040C),
+	LANG("French - Belgium", "fr_be", 0x080C),
+	LANG("French - Canada", "fr_ca", 0x0C0C),
+	LANG("French - Luxembourg", "fr_lu", 0x140C),
+	LANG("French - Switzerland", "fr_ch", 0x100C),
+	LANG("Gaelic - Ireland", "gd_ie", 0x083C),
+	LANG("Gaelic - Scotland", "gd", 0x043C),
+	LANG("German - Germany", "de_de", 0x0407),
+	LANG("German - Austria", "de_at", 0x0C07),
+	LANG("German - Liechtenstein", "de_li", 0x1407),
+	LANG("German - Luxembourg", "de_lu", 0x1007),
+	LANG("German - Switzerland", "de_ch", 0x0807),
+	LANG("Greek", "el", 0x0408),
+	LANG("Hebrew", "he", 0x040D),
+	LANG("Hindi", "hi", 0x0439),
+	LANG("Hungarian", "hu", 0x040E),
+	LANG("Icelandic", "is", 0x040F),
+	LANG("Indonesian", "id", 0x0421),
+	LANG("Italian - Italy", "it_it", 0x0410),
+	LANG("Italian - Switzerland", "it_ch", 0x0810),
+	LANG("Japanese", "ja", 0x0411),
+	LANG("Korean", "ko", 0x0412),
+	LANG("Latvian", "lv", 0x0426),
+	LANG("Lithuanian", "lt", 0x0427),
+	LANG("F.Y.R.O. Macedonia", "mk", 0x042F),
+	LANG("Malay - Malaysia", "ms_my", 0x043E),
+	LANG("Malay – Brunei", "ms_bn", 0x083E),
+	LANG("Maltese", "mt", 0x043A),
+	LANG("Marathi", "mr", 0x044E),
+	LANG("Norwegian - Bokml", "no_no", 0x0414),
+	LANG("Norwegian - Nynorsk", "no_no", 0x0814),
+	LANG("Polish", "pl", 0x0415),
+	LANG("Portuguese - Portugal", "pt_pt", 0x0816),
+	LANG("Portuguese - Brazil", "pt_br", 0x0416),
+	LANG("Raeto-Romance", "rm", 0x0417),
+	LANG("Romanian - Romania", "ro", 0x0418),
+	LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818),
+	LANG("Russian", "ru", 0x0419),
+	LANG("Russian - Republic of Moldova", "ru_mo", 0x0819),
+	LANG("Sanskrit", "sa", 0x044F),
+	LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A),
+	LANG("Serbian - Latin", "sr_sp", 0x081A),
+	LANG("Setsuana", "tn", 0x0432),
+	LANG("Slovenian", "sl", 0x0424),
+	LANG("Slovak", "sk", 0x041B),
+	LANG("Sorbian", "sb", 0x042E),
+	LANG("Spanish - Spain (Traditional)", "es_es", 0x040A),
+	LANG("Spanish - Argentina", "es_ar", 0x2C0A),
+	LANG("Spanish - Bolivia", "es_bo", 0x400A),
+	LANG("Spanish - Chile", "es_cl", 0x340A),
+	LANG("Spanish - Colombia", "es_co", 0x240A),
+	LANG("Spanish - Costa Rica", "es_cr", 0x140A),
+	LANG("Spanish - Dominican Republic", "es_do", 0x1C0A),
+	LANG("Spanish - Ecuador", "es_ec", 0x300A),
+	LANG("Spanish - Guatemala", "es_gt", 0x100A),
+	LANG("Spanish - Honduras", "es_hn", 0x480A),
+	LANG("Spanish - Mexico", "es_mx", 0x080A),
+	LANG("Spanish - Nicaragua", "es_ni", 0x4C0A),
+	LANG("Spanish - Panama", "es_pa", 0x180A),
+	LANG("Spanish - Peru", "es_pe", 0x280A),
+	LANG("Spanish - Puerto Rico", "es_pr", 0x500A),
+	LANG("Spanish - Paraguay", "es_py", 0x3C0A),
+	LANG("Spanish - El Salvador", "es_sv", 0x440A),
+	LANG("Spanish - Uruguay", "es_uy", 0x380A),
+	LANG("Spanish - Venezuela", "es_ve", 0x200A),
+	LANG("Southern Sotho", "st", 0x0430),
+	LANG("Swahili", "sw", 0x0441),
+	LANG("Swedish - Sweden", "sv_se", 0x041D),
+	LANG("Swedish - Finland", "sv_fi", 0x081D),
+	LANG("Tamil", "ta", 0x0449),
+	LANG("Tatar", "tt", 0X0444),
+	LANG("Thai", "th", 0x041E),
+	LANG("Turkish", "tr", 0x041F),
+	LANG("Tsonga", "ts", 0x0431),
+	LANG("Ukrainian", "uk", 0x0422),
+	LANG("Urdu", "ur", 0x0420),
+	LANG("Uzbek - Cyrillic", "uz_uz", 0x0843),
+	LANG("Uzbek – Latin", "uz_uz", 0x0443),
+	LANG("Vietnamese", "vi", 0x042A),
+	LANG("Xhosa", "xh", 0x0434),
+	LANG("Yiddish", "yi", 0x043D),
+	LANG("Zulu", "zu", 0x0435),
+	LANG(NULL, NULL, 0x0),
+};
+
+uint16_t get_usb_code_for_current_locale(void)
+{
+	char *locale;
+	char search_string[64];
+	char *ptr;
+	struct lang_map_entry *lang;
+
+	/* Get the current locale. */
+	locale = setlocale(0, NULL);
+	if (!locale)
+		return 0x0;
+
+	/* Make a copy of the current locale string. */
+	strncpy(search_string, locale, sizeof(search_string));
+	search_string[sizeof(search_string)-1] = '\0';
+
+	/* Chop off the encoding part, and make it lower case. */
+	ptr = search_string;
+	while (*ptr) {
+		*ptr = tolower(*ptr);
+		if (*ptr == '.') {
+			*ptr = '\0';
+			break;
+		}
+		ptr++;
+	}
+
+	/* Find the entry which matches the string code of our locale. */
+	lang = lang_map;
+	while (lang->string_code) {
+		if (!strcmp(lang->string_code, search_string)) {
+			return lang->usb_code;
+		}
+		lang++;
+	}
+
+	/* There was no match. Find with just the language only. */
+	/* Chop off the variant. Chop it off at the '_'. */
+	ptr = search_string;
+	while (*ptr) {
+		*ptr = tolower(*ptr);
+		if (*ptr == '_') {
+			*ptr = '\0';
+			break;
+		}
+		ptr++;
+	}
+
+#if 0 /* TODO: Do we need this? */
+	/* Find the entry which matches the string code of our language. */
+	lang = lang_map;
+	while (lang->string_code) {
+		if (!strcmp(lang->string_code, search_string)) {
+			return lang->usb_code;
+		}
+		lang++;
+	}
+#endif
+
+	/* Found nothing. */
+	return 0x0;
+}
+
+#if defined(__cplusplus) && !defined(NAMESPACE)
+}
+#endif
+
+#ifdef NAMESPACE
+}
+#endif
+
+#endif /* SDL_JOYSTICK_HIDAPI */
diff --git a/source/src/hidapi/libusb/hidusb.cpp b/source/src/hidapi/libusb/hidusb.cpp
new file mode 100644
index 0000000..5006306
--- /dev/null
+++ b/source/src/hidapi/libusb/hidusb.cpp
@@ -0,0 +1,3 @@
+
+#define NAMESPACE HIDUSB
+#include "hid.c"
diff --git a/source/src/hidapi/linux/Makefile-manual b/source/src/hidapi/linux/Makefile-manual
new file mode 100644
index 0000000..04ce1de
--- /dev/null
+++ b/source/src/hidapi/linux/Makefile-manual
@@ -0,0 +1,49 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidtest-hidraw libs
+
+libs: libhidapi-hidraw.so
+
+CC       ?= gcc
+CFLAGS   ?= -Wall -g -fpic
+
+CXX      ?= g++
+CXXFLAGS ?= -Wall -g -fpic
+
+LDFLAGS  ?= -Wall -g
+
+
+COBJS     = hid.o
+CPPOBJS   = ../hidtest/hidtest.o
+OBJS      = $(COBJS) $(CPPOBJS)
+LIBS_UDEV = `pkg-config libudev --libs` -lrt
+LIBS      = $(LIBS_UDEV)
+INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
+
+
+# Console Test Program
+hidtest-hidraw: $(COBJS) $(CPPOBJS)
+	$(CXX) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@
+
+# Shared Libs
+libhidapi-hidraw.so: $(COBJS)
+	$(CC) $(LDFLAGS) $(LIBS_UDEV) -shared -fpic -Wl,-soname,$@.0 $^ -o $@
+
+# Objects
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
+
+
+clean:
+	rm -f $(OBJS) hidtest-hidraw libhidapi-hidraw.so ../hidtest/hidtest.o
+
+.PHONY: clean libs
diff --git a/source/src/hidapi/linux/Makefile.am b/source/src/hidapi/linux/Makefile.am
new file mode 100644
index 0000000..230eeb7
--- /dev/null
+++ b/source/src/hidapi/linux/Makefile.am
@@ -0,0 +1,10 @@
+lib_LTLIBRARIES = libhidapi-hidraw.la
+libhidapi_hidraw_la_SOURCES = hid.c
+libhidapi_hidraw_la_LDFLAGS = $(LTLDFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ $(CFLAGS_HIDRAW)
+libhidapi_hidraw_la_LIBADD = $(LIBS_HIDRAW)
+
+hdrdir = $(includedir)/hidapi
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+
+EXTRA_DIST = Makefile-manual
diff --git a/source/src/hidapi/linux/README.txt b/source/src/hidapi/linux/README.txt
new file mode 100644
index 0000000..8006694
--- /dev/null
+++ b/source/src/hidapi/linux/README.txt
@@ -0,0 +1,59 @@
+
+There are two implementations of HIDAPI for Linux. One (linux/hid.c) uses the
+Linux hidraw driver, and the other (libusb/hid.c) uses libusb. Which one you
+use depends on your application. Complete functionality of the hidraw
+version depends on patches to the Linux kernel which are not currently in
+the mainline. These patches have to do with sending and receiving feature
+reports. The libusb implementation uses libusb to talk directly to the
+device, bypassing any Linux HID driver. The disadvantage of the libusb
+version is that it will only work with USB devices, while the hidraw
+implementation will work with Bluetooth devices as well.
+
+To use HIDAPI, simply drop either linux/hid.c or libusb/hid.c into your
+application and build using the build parameters in the Makefile.
+
+
+Libusb Implementation notes
+----------------------------
+For the libusb implementation, libusb-1.0 must be installed. Libusb 1.0 is
+different than the legacy libusb 0.1 which is installed on many systems. To
+install libusb-1.0 on Ubuntu and other Debian-based systems, run:
+	sudo apt-get install libusb-1.0-0-dev
+
+
+Hidraw Implementation notes
+----------------------------
+For the hidraw implementation, libudev headers and libraries are required to
+build hidapi programs.  To install libudev libraries on Ubuntu,
+and other Debian-based systems, run:
+	sudo apt-get install libudev-dev
+
+On Redhat-based systems, run the following as root:
+	yum install libudev-devel
+
+Unfortunately, the hidraw driver, which the linux version of hidapi is based
+on, contains bugs in kernel versions < 2.6.36, which the client application
+should be aware of.
+
+Bugs (hidraw implementation only):
+-----------------------------------
+On Kernel versions < 2.6.34, if your device uses numbered reports, an extra
+byte will be returned at the beginning of all reports returned from read()
+for hidraw devices. This is worked around in the libary. No action should be
+necessary in the client library.
+
+On Kernel versions < 2.6.35, reports will only be sent using a Set_Report
+transfer on the CONTROL endpoint. No data will ever be sent on an Interrupt
+Out endpoint if one exists. This is fixed in 2.6.35. In 2.6.35, OUTPUT
+reports will be sent to the device on the first INTERRUPT OUT endpoint if it
+exists; If it does not exist, OUTPUT reports will be sent on the CONTROL
+endpoint.
+
+On Kernel versions < 2.6.36, add an extra byte containing the report number
+to sent reports if numbered reports are used, and the device does not
+contain an INTERRPUT OUT endpoint for OUTPUT transfers.  For example, if
+your device uses numbered reports and wants to send {0x2 0xff 0xff 0xff} to
+the device (0x2 is the report number), you must send {0x2 0x2 0xff 0xff
+0xff}. If your device has the optional Interrupt OUT endpoint, this does not
+apply (but really on 2.6.35 only, because 2.6.34 won't use the interrupt
+out endpoint).
diff --git a/source/src/hidapi/linux/hid.c b/source/src/hidapi/linux/hid.c
new file mode 100644
index 0000000..b78e009
--- /dev/null
+++ b/source/src/hidapi/linux/hid.c
@@ -0,0 +1,898 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Alan Ott
+ Signal 11 Software
+
+ 8/22/2009
+ Linux Version - 6/2/2009
+
+ Copyright 2009, All Rights Reserved.
+
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        http://github.com/signal11/hidapi .
+********************************************************/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
+#endif
+
+/* C */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <errno.h>
+
+/* Unix */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <poll.h>
+
+/* Linux */
+#include <linux/hidraw.h>
+#include <linux/version.h>
+#include <linux/input.h>
+#include <libudev.h>
+
+#include "hidapi.h"
+
+#ifdef NAMESPACE
+namespace NAMESPACE
+{
+#endif
+
+/* Definitions from linux/hidraw.h. Since these are new, some distros
+   may not have header files which contain them. */
+#ifndef HIDIOCSFEATURE
+#define HIDIOCSFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
+#endif
+#ifndef HIDIOCGFEATURE
+#define HIDIOCGFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
+#endif
+
+/* USB HID device property names */
+const char *device_string_names[] = {
+	"manufacturer",
+	"product",
+	"serial",
+};
+
+/* Symbolic names for the properties above */
+enum device_string_id {
+	DEVICE_STRING_MANUFACTURER,
+	DEVICE_STRING_PRODUCT,
+	DEVICE_STRING_SERIAL,
+
+	DEVICE_STRING_COUNT,
+};
+
+struct hid_device_ {
+	int device_handle;
+	int blocking;
+	int uses_numbered_reports;
+	int is_bluetooth;
+};
+
+
+static __u32 kernel_version = 0;
+
+static __u32 detect_kernel_version(void)
+{
+	struct utsname name;
+	int major, minor, release;
+	int ret;
+
+	uname(&name);
+	ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release);
+	if (ret == 3) {
+		return KERNEL_VERSION(major, minor, release);
+	}
+
+	ret = sscanf(name.release, "%d.%d", &major, &minor);
+	if (ret == 2) {
+		return KERNEL_VERSION(major, minor, 0);
+	}
+
+	printf("Couldn't determine kernel version from version string \"%s\"\n", name.release);
+	return 0;
+}
+
+static hid_device *new_hid_device(void)
+{
+	hid_device *dev = (hid_device *)calloc(1, sizeof(hid_device));
+	dev->device_handle = -1;
+	dev->blocking = 1;
+	dev->uses_numbered_reports = 0;
+	dev->is_bluetooth = 0;
+
+	return dev;
+}
+
+
+/* The caller must free the returned string with free(). */
+static wchar_t *utf8_to_wchar_t(const char *utf8)
+{
+	wchar_t *ret = NULL;
+
+	if (utf8) {
+		size_t wlen = mbstowcs(NULL, utf8, 0);
+		if ((size_t) -1 == wlen) {
+			return wcsdup(L"");
+		}
+		ret = (wchar_t *)calloc(wlen+1, sizeof(wchar_t));
+		mbstowcs(ret, utf8, wlen+1);
+		ret[wlen] = 0x0000;
+	}
+
+	return ret;
+}
+
+/* Get an attribute value from a udev_device and return it as a whar_t
+   string. The returned string must be freed with free() when done.*/
+static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
+{
+	return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name));
+}
+
+/* uses_numbered_reports() returns 1 if report_descriptor describes a device
+   which contains numbered reports. */
+static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
+	unsigned int i = 0;
+	int size_code;
+	int data_len, key_size;
+
+	while (i < size) {
+		int key = report_descriptor[i];
+
+		/* Check for the Report ID key */
+		if (key == 0x85/*Report ID*/) {
+			/* This device has a Report ID, which means it uses
+			   numbered reports. */
+			return 1;
+		}
+
+		//printf("key: %02hhx\n", key);
+
+		if ((key & 0xf0) == 0xf0) {
+			/* This is a Long Item. The next byte contains the
+			   length of the data section (value) for this key.
+			   See the HID specification, version 1.11, section
+			   6.2.2.3, titled "Long Items." */
+			if (i+1 < size)
+				data_len = report_descriptor[i+1];
+			else
+				data_len = 0; /* malformed report */
+			key_size = 3;
+		}
+		else {
+			/* This is a Short Item. The bottom two bits of the
+			   key contain the size code for the data section
+			   (value) for this key.  Refer to the HID
+			   specification, version 1.11, section 6.2.2.2,
+			   titled "Short Items." */
+			size_code = key & 0x3;
+			switch (size_code) {
+			case 0:
+			case 1:
+			case 2:
+				data_len = size_code;
+				break;
+			case 3:
+				data_len = 4;
+				break;
+			default:
+				/* Can't ever happen since size_code is & 0x3 */
+				data_len = 0;
+				break;
+			};
+			key_size = 1;
+		}
+
+		/* Skip over this key and it's associated data */
+		i += data_len + key_size;
+	}
+
+	/* Didn't find a Report ID key. Device doesn't use numbered reports. */
+	return 0;
+}
+
+/*
+ * The caller is responsible for free()ing the (newly-allocated) character
+ * strings pointed to by serial_number_utf8 and product_name_utf8 after use.
+ */
+static int
+parse_uevent_info(const char *uevent, int *bus_type,
+	unsigned short *vendor_id, unsigned short *product_id,
+	char **serial_number_utf8, char **product_name_utf8)
+{
+	char *tmp = strdup(uevent);
+	char *saveptr = NULL;
+	char *line;
+	char *key;
+	char *value;
+
+	int found_id = 0;
+	int found_serial = 0;
+	int found_name = 0;
+
+	line = strtok_r(tmp, "\n", &saveptr);
+	while (line != NULL) {
+		/* line: "KEY=value" */
+		key = line;
+		value = strchr(line, '=');
+		if (!value) {
+			goto next_line;
+		}
+		*value = '\0';
+		value++;
+
+		if (strcmp(key, "HID_ID") == 0) {
+			/**
+			 *        type vendor   product
+			 * HID_ID=0003:000005AC:00008242
+			 **/
+			int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id);
+			if (ret == 3) {
+				found_id = 1;
+			}
+		} else if (strcmp(key, "HID_NAME") == 0) {
+			/* The caller has to free the product name */
+			*product_name_utf8 = strdup(value);
+			found_name = 1;
+		} else if (strcmp(key, "HID_UNIQ") == 0) {
+			/* The caller has to free the serial number */
+			*serial_number_utf8 = strdup(value);
+			found_serial = 1;
+		}
+
+next_line:
+		line = strtok_r(NULL, "\n", &saveptr);
+	}
+
+	free(tmp);
+	return (found_id && found_name && found_serial);
+}
+
+static int is_bluetooth(hid_device *dev)
+{
+	struct udev *udev;
+	struct udev_device *udev_dev, *hid_dev;
+	struct stat s;
+	int ret = -1;
+
+	/* Create the udev object */
+	udev = udev_new();
+	if (!udev) {
+		printf("Can't create udev\n");
+		return -1;
+	}
+
+	/* Get the dev_t (major/minor numbers) from the file handle. */
+	ret = fstat(dev->device_handle, &s);
+	if (-1 == ret) {
+		udev_unref(udev);
+		return ret;
+	}
+
+	/* Open a udev device from the dev_t. 'c' means character device. */
+	udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
+	if (udev_dev) {
+		hid_dev = udev_device_get_parent_with_subsystem_devtype(
+			udev_dev,
+			"hid",
+			NULL);
+		if (hid_dev) {
+			unsigned short dev_vid;
+			unsigned short dev_pid;
+			int bus_type;
+			char *serial_number_utf8 = NULL;
+			char *product_name_utf8 = NULL;
+
+			ret = parse_uevent_info(
+			           udev_device_get_sysattr_value(hid_dev, "uevent"),
+			           &bus_type,
+			           &dev_vid,
+			           &dev_pid,
+			           &serial_number_utf8,
+			           &product_name_utf8);
+			free(serial_number_utf8);
+			free(product_name_utf8);
+
+			ret = (bus_type == BUS_BLUETOOTH);
+
+			/* hid_dev doesn't need to be (and can't be) unref'd.
+			   I'm not sure why, but it'll throw double-free() errors. */
+		}
+		udev_device_unref(udev_dev);
+	}
+
+	udev_unref(udev);
+
+	return ret;
+}
+
+
+static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen)
+{
+	struct udev *udev;
+	struct udev_device *udev_dev, *parent, *hid_dev;
+	struct stat s;
+	int ret = -1;
+        char *serial_number_utf8 = NULL;
+        char *product_name_utf8 = NULL;
+	char *tmp;
+
+	/* Create the udev object */
+	udev = udev_new();
+	if (!udev) {
+		printf("Can't create udev\n");
+		return -1;
+	}
+
+	/* Get the dev_t (major/minor numbers) from the file handle. */
+	ret = fstat(dev->device_handle, &s);
+	if (-1 == ret) {
+		udev_unref(udev);
+		return ret;
+	}
+	/* Open a udev device from the dev_t. 'c' means character device. */
+	udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
+	if (udev_dev) {
+		hid_dev = udev_device_get_parent_with_subsystem_devtype(
+			udev_dev,
+			"hid",
+			NULL);
+		if (hid_dev) {
+			unsigned short dev_vid;
+			unsigned short dev_pid;
+			int bus_type;
+			size_t retm;
+
+			ret = parse_uevent_info(
+			           udev_device_get_sysattr_value(hid_dev, "uevent"),
+			           &bus_type,
+			           &dev_vid,
+			           &dev_pid,
+			           &serial_number_utf8,
+			           &product_name_utf8);
+
+			if (bus_type == BUS_BLUETOOTH) {
+				switch (key) {
+					case DEVICE_STRING_MANUFACTURER:
+						wcsncpy(string, L"", maxlen);
+						ret = 0;
+						break;
+					case DEVICE_STRING_PRODUCT:
+						retm = mbstowcs(string, product_name_utf8, maxlen);
+						ret = (retm == (size_t)-1)? -1: 0;
+						break;
+					case DEVICE_STRING_SERIAL:
+						/* Bluetooth serial numbers are often the bluetooth device address
+						   and we want that with the colons stripped out, which is the correct
+						   serial number for PS4 controllers
+						 */
+						while ((tmp = strchr(serial_number_utf8, ':')) != NULL) {
+							memmove(tmp, tmp+1, strlen(tmp));
+						}
+						retm = mbstowcs(string, serial_number_utf8, maxlen);
+						ret = (retm == (size_t)-1)? -1: 0;
+						break;
+					case DEVICE_STRING_COUNT:
+					default:
+						ret = -1;
+						break;
+				}
+			}
+			else {
+				/* This is a USB device. Find its parent USB Device node. */
+				parent = udev_device_get_parent_with_subsystem_devtype(
+					   udev_dev,
+					   "usb",
+					   "usb_device");
+				if (parent) {
+					const char *str;
+					const char *key_str = NULL;
+
+					if (key >= 0 && key < DEVICE_STRING_COUNT) {
+						key_str = device_string_names[key];
+					} else {
+						ret = -1;
+						goto end;
+					}
+
+					str = udev_device_get_sysattr_value(parent, key_str);
+					if (str) {
+						/* Convert the string from UTF-8 to wchar_t */
+						retm = mbstowcs(string, str, maxlen);
+						ret = (retm == (size_t)-1)? -1: 0;
+						goto end;
+					}
+				}
+			}
+		}
+	}
+
+end:
+        free(serial_number_utf8);
+        free(product_name_utf8);
+
+	udev_device_unref(udev_dev);
+	/* parent and hid_dev don't need to be (and can't be) unref'd.
+	   I'm not sure why, but they'll throw double-free() errors. */
+	udev_unref(udev);
+
+	return ret;
+}
+
+int HID_API_EXPORT hid_init(void)
+{
+	const char *locale;
+
+	/* Set the locale if it's not set. */
+	locale = setlocale(LC_CTYPE, NULL);
+	if (!locale)
+		setlocale(LC_CTYPE, "");
+
+	kernel_version = detect_kernel_version();
+
+	return 0;
+}
+
+int HID_API_EXPORT hid_exit(void)
+{
+	/* Nothing to do for this in the Linux/hidraw implementation. */
+	return 0;
+}
+
+
+struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+	struct udev *udev;
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *devices, *dev_list_entry;
+
+	struct hid_device_info *root = NULL; /* return object */
+	struct hid_device_info *cur_dev = NULL;
+	struct hid_device_info *prev_dev = NULL; /* previous device */
+
+	hid_init();
+
+	/* Create the udev object */
+	udev = udev_new();
+	if (!udev) {
+		printf("Can't create udev\n");
+		return NULL;
+	}
+
+	/* Create a list of the devices in the 'hidraw' subsystem. */
+	enumerate = udev_enumerate_new(udev);
+	udev_enumerate_add_match_subsystem(enumerate, "hidraw");
+	udev_enumerate_scan_devices(enumerate);
+	devices = udev_enumerate_get_list_entry(enumerate);
+	/* For each item, see if it matches the vid/pid, and if so
+	   create a udev_device record for it */
+	udev_list_entry_foreach(dev_list_entry, devices) {
+		const char *sysfs_path;
+		const char *dev_path;
+		const char *str;
+		struct udev_device *raw_dev; /* The device's hidraw udev node. */
+		struct udev_device *hid_dev; /* The device's HID udev node. */
+		struct udev_device *usb_dev; /* The device's USB udev node. */
+		struct udev_device *intf_dev; /* The device's interface (in the USB sense). */
+		unsigned short dev_vid;
+		unsigned short dev_pid;
+		char *serial_number_utf8 = NULL;
+		char *product_name_utf8 = NULL;
+		int bus_type;
+		int result;
+
+		/* Get the filename of the /sys entry for the device
+		   and create a udev_device object (dev) representing it */
+		sysfs_path = udev_list_entry_get_name(dev_list_entry);
+		raw_dev = udev_device_new_from_syspath(udev, sysfs_path);
+		dev_path = udev_device_get_devnode(raw_dev);
+
+		hid_dev = udev_device_get_parent_with_subsystem_devtype(
+			raw_dev,
+			"hid",
+			NULL);
+
+		if (!hid_dev) {
+			/* Unable to find parent hid device. */
+			goto next;
+		}
+
+		result = parse_uevent_info(
+			udev_device_get_sysattr_value(hid_dev, "uevent"),
+			&bus_type,
+			&dev_vid,
+			&dev_pid,
+			&serial_number_utf8,
+			&product_name_utf8);
+
+		if (!result) {
+			/* parse_uevent_info() failed for at least one field. */
+			goto next;
+		}
+
+		if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) {
+			/* We only know how to handle USB and BT devices. */
+			goto next;
+		}
+
+		if (access(dev_path, R_OK|W_OK) != 0) {
+			/* We can't open this device, ignore it */
+			goto next;
+		}
+
+		/* Check the VID/PID against the arguments */
+		if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
+		    (product_id == 0x0 || product_id == dev_pid)) {
+			struct hid_device_info *tmp;
+
+			/* VID/PID match. Create the record. */
+			tmp = (struct hid_device_info *)malloc(sizeof(struct hid_device_info));
+			if (cur_dev) {
+				cur_dev->next = tmp;
+			}
+			else {
+				root = tmp;
+			}
+			prev_dev = cur_dev;
+			cur_dev = tmp;
+
+			/* Fill out the record */
+			cur_dev->next = NULL;
+			cur_dev->path = dev_path? strdup(dev_path): NULL;
+
+			/* VID/PID */
+			cur_dev->vendor_id = dev_vid;
+			cur_dev->product_id = dev_pid;
+
+			/* Serial Number */
+			cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8);
+
+			/* Release Number */
+			cur_dev->release_number = 0x0;
+
+			/* Interface Number */
+			cur_dev->interface_number = -1;
+
+			switch (bus_type) {
+				case BUS_USB:
+					/* The device pointed to by raw_dev contains information about
+					   the hidraw device. In order to get information about the
+					   USB device, get the parent device with the
+					   subsystem/devtype pair of "usb"/"usb_device". This will
+					   be several levels up the tree, but the function will find
+					   it. */
+					usb_dev = udev_device_get_parent_with_subsystem_devtype(
+							raw_dev,
+							"usb",
+							"usb_device");
+
+					if (!usb_dev) {
+						/* Free this device */
+						free(cur_dev->serial_number);
+						free(cur_dev->path);
+						free(cur_dev);
+
+						/* Take it off the device list. */
+						if (prev_dev) {
+							prev_dev->next = NULL;
+							cur_dev = prev_dev;
+						}
+						else {
+							cur_dev = root = NULL;
+						}
+
+						goto next;
+					}
+
+					/* Manufacturer and Product strings */
+					cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]);
+					cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]);
+
+					/* Release Number */
+					str = udev_device_get_sysattr_value(usb_dev, "bcdDevice");
+					cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0;
+
+					/* Get a handle to the interface's udev node. */
+					intf_dev = udev_device_get_parent_with_subsystem_devtype(
+							raw_dev,
+							"usb",
+							"usb_interface");
+					if (intf_dev) {
+						str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber");
+						cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1;
+					}
+
+					break;
+
+				case BUS_BLUETOOTH:
+					/* Manufacturer and Product strings */
+					cur_dev->manufacturer_string = wcsdup(L"");
+					cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
+
+					break;
+
+				default:
+					/* Unknown device type - this should never happen, as we
+					 * check for USB and Bluetooth devices above */
+					break;
+			}
+		}
+
+	next:
+		free(serial_number_utf8);
+		free(product_name_utf8);
+		udev_device_unref(raw_dev);
+		/* hid_dev, usb_dev and intf_dev don't need to be (and can't be)
+		   unref()d.  It will cause a double-free() error.  I'm not
+		   sure why.  */
+	}
+	/* Free the enumerator and udev objects. */
+	udev_enumerate_unref(enumerate);
+	udev_unref(udev);
+
+	return root;
+}
+
+void  HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
+{
+	struct hid_device_info *d = devs;
+	while (d) {
+		struct hid_device_info *next = d->next;
+		free(d->path);
+		free(d->serial_number);
+		free(d->manufacturer_string);
+		free(d->product_string);
+		free(d);
+		d = next;
+	}
+}
+
+hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
+{
+	struct hid_device_info *devs, *cur_dev;
+	const char *path_to_open = NULL;
+	hid_device *handle = NULL;
+
+	devs = hid_enumerate(vendor_id, product_id);
+	cur_dev = devs;
+	while (cur_dev) {
+		if (cur_dev->vendor_id == vendor_id &&
+		    cur_dev->product_id == product_id) {
+			if (serial_number) {
+				if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
+					path_to_open = cur_dev->path;
+					break;
+				}
+			}
+			else {
+				path_to_open = cur_dev->path;
+				break;
+			}
+		}
+		cur_dev = cur_dev->next;
+	}
+
+	if (path_to_open) {
+		/* Open the device */
+		handle = hid_open_path(path_to_open, 0);
+	}
+
+	hid_free_enumeration(devs);
+
+	return handle;
+}
+
+hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
+{
+	hid_device *dev = NULL;
+
+	hid_init();
+
+	dev = new_hid_device();
+
+	/* OPEN HERE */
+	dev->device_handle = open(path, O_RDWR);
+
+	/* If we have a good handle, return it. */
+	if (dev->device_handle > 0) {
+
+		/* Get the report descriptor */
+		int res, desc_size = 0;
+		struct hidraw_report_descriptor rpt_desc;
+
+		memset(&rpt_desc, 0x0, sizeof(rpt_desc));
+
+		/* Get Report Descriptor Size */
+		res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
+		if (res < 0)
+			perror("HIDIOCGRDESCSIZE");
+
+
+		/* Get Report Descriptor */
+		rpt_desc.size = desc_size;
+		res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
+		if (res < 0) {
+			perror("HIDIOCGRDESC");
+		} else {
+			/* Determine if this device uses numbered reports. */
+			dev->uses_numbered_reports =
+				uses_numbered_reports(rpt_desc.value,
+				                      rpt_desc.size);
+		}
+
+		dev->is_bluetooth = (is_bluetooth(dev) == 1);
+
+		return dev;
+	}
+	else {
+		/* Unable to open any devices. */
+		free(dev);
+		return NULL;
+	}
+}
+
+
+int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
+{
+	int bytes_written;
+
+	bytes_written = write(dev->device_handle, data, length);
+
+	return bytes_written;
+}
+
+
+int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
+{
+	int bytes_read;
+
+	if (milliseconds >= 0) {
+		/* Milliseconds is either 0 (non-blocking) or > 0 (contains
+		   a valid timeout). In both cases we want to call poll()
+		   and wait for data to arrive.  Don't rely on non-blocking
+		   operation (O_NONBLOCK) since some kernels don't seem to
+		   properly report device disconnection through read() when
+		   in non-blocking mode.  */
+		int ret;
+		struct pollfd fds;
+
+		fds.fd = dev->device_handle;
+		fds.events = POLLIN;
+		fds.revents = 0;
+		ret = poll(&fds, 1, milliseconds);
+		if (ret == -1 || ret == 0) {
+			/* Error or timeout */
+			return ret;
+		}
+		else {
+			/* Check for errors on the file descriptor. This will
+			   indicate a device disconnection. */
+			if (fds.revents & (POLLERR | POLLHUP | POLLNVAL))
+				return -1;
+		}
+	}
+
+	bytes_read = read(dev->device_handle, data, length);
+	if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS))
+		bytes_read = 0;
+
+	if (bytes_read >= 0 &&
+	    kernel_version != 0 &&
+	    kernel_version < KERNEL_VERSION(2,6,34) &&
+	    dev->uses_numbered_reports) {
+		/* Work around a kernel bug. Chop off the first byte. */
+		memmove(data, data+1, bytes_read);
+		bytes_read--;
+	}
+
+	return bytes_read;
+}
+
+int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
+{
+	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
+}
+
+int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
+{
+	/* Do all non-blocking in userspace using poll(), since it looks
+	   like there's a bug in the kernel in some versions where
+	   read() will not return -1 on disconnection of the USB device */
+
+	dev->blocking = !nonblock;
+	return 0; /* Success */
+}
+
+
+int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
+{
+	int res;
+
+	res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
+	if (res < 0)
+		perror("ioctl (SFEATURE)");
+
+	return res;
+}
+
+int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+{
+	int res;
+
+	/* It looks like HIDIOCGFEATURE() on Bluetooth devices doesn't return the report number */
+	if (dev->is_bluetooth) {
+		data[1] = data[0];
+		++data;
+		--length;
+	}
+	res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
+	if (res < 0)
+		perror("ioctl (GFEATURE)");
+	else if (dev->is_bluetooth)
+		++res;
+
+	return res;
+}
+
+
+void HID_API_EXPORT hid_close(hid_device *dev)
+{
+	if (!dev)
+		return;
+	close(dev->device_handle);
+	free(dev);
+}
+
+
+int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
+{
+	return -1;
+}
+
+
+HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
+{
+	return NULL;
+}
+
+#ifdef NAMESPACE
+}
+#endif
+
+#endif /* SDL_JOYSTICK_HIDAPI */
diff --git a/source/src/hidapi/linux/hid.cpp b/source/src/hidapi/linux/hid.cpp
new file mode 100644
index 0000000..841f34f
--- /dev/null
+++ b/source/src/hidapi/linux/hid.cpp
@@ -0,0 +1,333 @@
+//=================== Copyright Valve Corporation, All rights reserved. =======
+//
+// Purpose: A wrapper around both the libusb and hidraw versions of HIDAPI
+//
+//          The libusb version doesn't support Bluetooth, but not all Linux
+//          distributions allow access to /dev/hidraw*
+//
+//          This merges the two, at a small performance cost, until distributions
+//          have granted access to /dev/hidraw*
+//
+//=============================================================================
+
+#define NAMESPACE HIDRAW
+#include "../hidapi/hidapi.h"
+#undef NAMESPACE
+#undef HIDAPI_H__
+
+#define NAMESPACE HIDUSB
+#include "../hidapi/hidapi.h"
+#undef NAMESPACE
+#undef HIDAPI_H__
+
+#include "../hidapi/hidapi.h"
+
+#include "../../../public/tier1/utlvector.h"
+#include "../../../public/tier1/utlhashmap.h"
+
+
+template <class T>
+void CopyHIDDeviceInfo( T *pSrc, struct hid_device_info *pDst )
+{
+	pDst->path = pSrc->path ? strdup( pSrc->path ) : NULL;
+	pDst->vendor_id = pSrc->vendor_id;
+	pDst->product_id = pSrc->product_id;
+	pDst->serial_number = pSrc->serial_number ? wcsdup( pSrc->serial_number ) : NULL;
+	pDst->release_number = pSrc->release_number;
+	pDst->manufacturer_string = pSrc->manufacturer_string ? wcsdup( pSrc->manufacturer_string ) : NULL;
+	pDst->product_string = pSrc->product_string ? wcsdup( pSrc->product_string ) : NULL;
+	pDst->usage_page = pSrc->usage_page;
+	pDst->usage = pSrc->usage;
+	pDst->interface_number = pSrc->interface_number;
+	pDst->next = NULL;
+}
+
+extern "C"
+{
+
+enum EHIDAPIType
+{
+	k_EHIDAPIUnknown,
+	k_EHIDAPIRAW,
+	k_EHIDAPIUSB
+};
+
+static CUtlHashMap<uintptr_t, EHIDAPIType> s_hashDeviceToAPI;
+
+static EHIDAPIType GetAPIForDevice( hid_device *pDevice )
+{
+	int iIndex = s_hashDeviceToAPI.Find( (uintptr_t)pDevice );
+	if ( iIndex != -1 )
+	{
+		return s_hashDeviceToAPI[ iIndex ];
+	}
+	return k_EHIDAPIUnknown;
+}
+
+struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+	struct HIDUSB::hid_device_info *usb_devs = HIDUSB::hid_enumerate( vendor_id, product_id );
+	struct HIDUSB::hid_device_info *usb_dev;
+	struct HIDRAW::hid_device_info *raw_devs = HIDRAW::hid_enumerate( vendor_id, product_id );
+	struct HIDRAW::hid_device_info *raw_dev;
+	struct hid_device_info *devs = NULL, *last = NULL, *new_dev;
+
+	for ( usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next )
+	{
+		bool bFound = false;
+		for ( raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next )
+		{
+			if ( usb_dev->vendor_id == raw_dev->vendor_id && usb_dev->product_id == raw_dev->product_id )
+			{
+				bFound = true;
+				break;
+			}
+		}
+
+//printf("%s USB device VID/PID 0x%.4x/0x%.4x, %ls %ls\n", bFound ? "Found matching" : "Added new", usb_dev->vendor_id, usb_dev->product_id, usb_dev->manufacturer_string, usb_dev->product_string );
+
+		if ( !bFound )
+		{
+			new_dev = new struct hid_device_info;
+			CopyHIDDeviceInfo( usb_dev, new_dev );
+
+			if ( last )
+			{
+				last->next = new_dev;
+			}
+			else
+			{
+				devs = new_dev;
+			}
+			last = new_dev;
+		}
+	}
+	HIDUSB::hid_free_enumeration( usb_devs );
+
+	for ( raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next )
+	{
+		new_dev = new struct hid_device_info;
+		CopyHIDDeviceInfo( raw_dev, new_dev );
+		new_dev->next = NULL;
+
+		if ( last )
+		{
+			last->next = new_dev;
+		}
+		else
+		{
+			devs = new_dev;
+		}
+		last = new_dev;
+	}
+	HIDRAW::hid_free_enumeration( raw_devs );
+
+	return devs;
+}
+
+void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
+{
+	while ( devs )
+	{
+		struct hid_device_info *next = devs->next;
+		free( devs->path );
+		free( devs->serial_number );
+		free( devs->manufacturer_string );
+		free( devs->product_string );
+		delete devs;
+		devs = next;
+	}
+}
+
+HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
+{
+	hid_device *pDevice = NULL;
+	if ( ( pDevice = (hid_device *)HIDRAW::hid_open( vendor_id, product_id, serial_number ) ) != NULL )
+	{
+		s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIRAW );
+		return pDevice;
+	}
+	if ( ( pDevice = (hid_device *)HIDUSB::hid_open( vendor_id, product_id, serial_number ) ) != NULL )
+	{
+		s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIUSB );
+		return pDevice;
+	}
+	return NULL;
+}
+
+HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
+{
+	hid_device *pDevice = NULL;
+	if ( ( pDevice = (hid_device *)HIDRAW::hid_open_path( path, bExclusive ) ) != NULL )
+	{
+		s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIRAW );
+		return pDevice;
+	}
+	if ( ( pDevice = (hid_device *)HIDUSB::hid_open_path( path, bExclusive ) ) != NULL )
+	{
+		s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIUSB );
+		return pDevice;
+	}
+	return NULL;
+}
+
+int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_write( (HIDRAW::hid_device*)device, data, length );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_write( (HIDUSB::hid_device*)device, data, length );
+	default:
+		return -1;
+	} 
+}
+
+int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_read_timeout( (HIDRAW::hid_device*)device, data, length, milliseconds );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_read_timeout( (HIDUSB::hid_device*)device, data, length, milliseconds );
+	default:
+		return -1;
+	} 
+}
+
+int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_read( (HIDRAW::hid_device*)device, data, length );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_read( (HIDUSB::hid_device*)device, data, length );
+	default:
+		return -1;
+	} 
+}
+
+int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_set_nonblocking( (HIDRAW::hid_device*)device, nonblock );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_set_nonblocking( (HIDUSB::hid_device*)device, nonblock );
+	default:
+		return -1;
+	} 
+}
+
+int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_send_feature_report( (HIDRAW::hid_device*)device, data, length );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_send_feature_report( (HIDUSB::hid_device*)device, data, length );
+	default:
+		return -1;
+	} 
+}
+
+int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_get_feature_report( (HIDRAW::hid_device*)device, data, length );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_get_feature_report( (HIDUSB::hid_device*)device, data, length );
+	default:
+		return -1;
+	} 
+}
+
+void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		HIDRAW::hid_close( (HIDRAW::hid_device*)device );
+		break;
+	case k_EHIDAPIUSB:
+		HIDUSB::hid_close( (HIDUSB::hid_device*)device );
+		break;
+	default:
+		break;
+	} 
+	s_hashDeviceToAPI.Remove( (uintptr_t)device );
+}
+
+int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_get_manufacturer_string( (HIDRAW::hid_device*)device, string, maxlen );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_get_manufacturer_string( (HIDUSB::hid_device*)device, string, maxlen );
+	default:
+		return -1;
+	} 
+}
+
+int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_get_product_string( (HIDRAW::hid_device*)device, string, maxlen );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_get_product_string( (HIDUSB::hid_device*)device, string, maxlen );
+	default:
+		return -1;
+	}
+}
+
+int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_get_serial_number_string( (HIDRAW::hid_device*)device, string, maxlen );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_get_serial_number_string( (HIDUSB::hid_device*)device, string, maxlen );
+	default:
+		return -1;
+	}
+}
+
+int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_get_indexed_string( (HIDRAW::hid_device*)device, string_index, string, maxlen );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_get_indexed_string( (HIDUSB::hid_device*)device, string_index, string, maxlen );
+	default:
+		return -1;
+	}
+}
+
+HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
+{
+	switch ( GetAPIForDevice( device ) )
+	{
+	case k_EHIDAPIRAW:
+		return HIDRAW::hid_error( (HIDRAW::hid_device*)device );
+	case k_EHIDAPIUSB:
+		return HIDUSB::hid_error( (HIDUSB::hid_device*)device );
+	default:
+		return NULL;
+	} 
+}
+
+}
diff --git a/source/src/hidapi/linux/hidraw.cpp b/source/src/hidapi/linux/hidraw.cpp
new file mode 100644
index 0000000..1bf6fad
--- /dev/null
+++ b/source/src/hidapi/linux/hidraw.cpp
@@ -0,0 +1,3 @@
+
+#define NAMESPACE HIDRAW
+#include "hid.c"
diff --git a/source/src/hidapi/m4/ax_pthread.m4 b/source/src/hidapi/m4/ax_pthread.m4
new file mode 100644
index 0000000..d90de34
--- /dev/null
+++ b/source/src/hidapi/m4/ax_pthread.m4
@@ -0,0 +1,309 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 18
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($ax_pthread_ok)
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT($attr_name)
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            ax_cv_PTHREAD_PRIO_INHERIT, [
+                AC_LINK_IFELSE([
+                    AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with xlc_r or cc_r
+        if test x"$GCC" != xyes; then
+          AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+        else
+          PTHREAD_CC=$CC
+        fi
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/source/src/hidapi/m4/pkg.m4 b/source/src/hidapi/m4/pkg.m4
new file mode 100644
index 0000000..0048a3f
--- /dev/null
+++ b/source/src/hidapi/m4/pkg.m4
@@ -0,0 +1,157 @@
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+		
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$PKG_CONFIG"; then
+    if test -n "$$1"; then
+        pkg_cv_[]$1="$$1"
+    else
+        PKG_CHECK_EXISTS([$3],
+                         [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+			 [pkg_failed=yes])
+    fi
+else
+	pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+		[AC_MSG_RESULT([no])
+                $4])
+elif test $pkg_failed = untried; then
+	ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
+		[$4])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/source/src/hidapi/mac/Makefile-manual b/source/src/hidapi/mac/Makefile-manual
new file mode 100644
index 0000000..5399b5a
--- /dev/null
+++ b/source/src/hidapi/mac/Makefile-manual
@@ -0,0 +1,32 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-07-03
+###########################################
+
+all: hidtest
+
+CC=gcc
+CXX=g++
+COBJS=hid.o
+CPPOBJS=../hidtest/hidtest.o
+OBJS=$(COBJS) $(CPPOBJS)
+CFLAGS+=-I../hidapi -Wall -g -c 
+LIBS=-framework IOKit -framework CoreFoundation
+
+
+hidtest: $(OBJS)
+	g++ -Wall -g $^ $(LIBS) -o hidtest
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+clean:
+	rm -f *.o hidtest $(CPPOBJS)
+
+.PHONY: clean
diff --git a/source/src/hidapi/mac/Makefile.am b/source/src/hidapi/mac/Makefile.am
new file mode 100644
index 0000000..23d96e0
--- /dev/null
+++ b/source/src/hidapi/mac/Makefile.am
@@ -0,0 +1,9 @@
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.c
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
+
+hdrdir = $(includedir)/hidapi
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+
+EXTRA_DIST = Makefile-manual
diff --git a/source/src/hidapi/mac/hid.c b/source/src/hidapi/mac/hid.c
new file mode 100644
index 0000000..d462d26
--- /dev/null
+++ b/source/src/hidapi/mac/hid.c
@@ -0,0 +1,1191 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+ 
+ Alan Ott
+ Signal 11 Software
+ 
+ 2010-07-03
+ 
+ Copyright 2010, All Rights Reserved.
+ 
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+ http://github.com/signal11/hidapi .
+ ********************************************************/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+/* See Apple Technical Note TN2187 for details on IOHidManager. */
+
+#include <IOKit/hid/IOHIDManager.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <wchar.h>
+#include <locale.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "hidapi.h"
+
+/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
+ It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
+ This implementation came from Brent Priddy and was posted on
+ StackOverflow. It is used with his permission. */
+typedef int pthread_barrierattr_t;
+typedef struct pthread_barrier {
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    int count;
+    int trip_count;
+} pthread_barrier_t;
+
+static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
+{
+	if(count == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+	
+	if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
+		return -1;
+	}
+	if(pthread_cond_init(&barrier->cond, 0) < 0) {
+		pthread_mutex_destroy(&barrier->mutex);
+		return -1;
+	}
+	barrier->trip_count = count;
+	barrier->count = 0;
+	
+	return 0;
+}
+
+static int pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+	pthread_cond_destroy(&barrier->cond);
+	pthread_mutex_destroy(&barrier->mutex);
+	return 0;
+}
+
+static int pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+	pthread_mutex_lock(&barrier->mutex);
+	++(barrier->count);
+	if(barrier->count >= barrier->trip_count)
+	{
+		barrier->count = 0;
+		pthread_cond_broadcast(&barrier->cond);
+		pthread_mutex_unlock(&barrier->mutex);
+		return 1;
+	}
+	else
+	{
+		pthread_cond_wait(&barrier->cond, &(barrier->mutex));
+		pthread_mutex_unlock(&barrier->mutex);
+		return 0;
+	}
+}
+
+static int return_data(hid_device *dev, unsigned char *data, size_t length);
+
+/* Linked List of input reports received from the device. */
+struct input_report {
+	uint8_t *data;
+	size_t len;
+	struct input_report *next;
+};
+
+struct hid_device_ {
+	IOHIDDeviceRef device_handle;
+	int blocking;
+	int uses_numbered_reports;
+	int disconnected;
+	CFStringRef run_loop_mode;
+	CFRunLoopRef run_loop;
+	CFRunLoopSourceRef source;
+	uint8_t *input_report_buf;
+	CFIndex max_input_report_len;
+	struct input_report *input_reports;
+	
+	pthread_t thread;
+	pthread_mutex_t mutex; /* Protects input_reports */
+	pthread_cond_t condition;
+	pthread_barrier_t barrier; /* Ensures correct startup sequence */
+	pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */
+	int shutdown_thread;
+	
+	hid_device *next;
+};
+
+/* Static list of all the devices open. This way when a device gets
+ disconnected, its hid_device structure can be marked as disconnected
+ from hid_device_removal_callback(). */
+static hid_device *device_list = NULL;
+static pthread_mutex_t device_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static hid_device *new_hid_device(void)
+{
+	hid_device *dev = (hid_device*)calloc(1, sizeof(hid_device));
+	dev->device_handle = NULL;
+	dev->blocking = 1;
+	dev->uses_numbered_reports = 0;
+	dev->disconnected = 0;
+	dev->run_loop_mode = NULL;
+	dev->run_loop = NULL;
+	dev->source = NULL;
+	dev->input_report_buf = NULL;
+	dev->input_reports = NULL;
+	dev->shutdown_thread = 0;
+	dev->next = NULL;
+	
+	/* Thread objects */
+	pthread_mutex_init(&dev->mutex, NULL);
+	pthread_cond_init(&dev->condition, NULL);
+	pthread_barrier_init(&dev->barrier, NULL, 2);
+	pthread_barrier_init(&dev->shutdown_barrier, NULL, 2);
+	
+	/* Add the new record to the device_list. */
+	pthread_mutex_lock(&device_list_mutex);
+	if (!device_list)
+		device_list = dev;
+	else {
+		hid_device *d = device_list;
+		while (d) {
+			if (!d->next) {
+				d->next = dev;
+				break;
+			}
+			d = d->next;
+		}
+	}
+	pthread_mutex_unlock(&device_list_mutex);
+	
+	return dev;
+}
+
+static void free_hid_device(hid_device *dev)
+{
+	if (!dev)
+		return;
+	
+	/* Delete any input reports still left over. */
+	struct input_report *rpt = dev->input_reports;
+	while (rpt) {
+		struct input_report *next = rpt->next;
+		free(rpt->data);
+		free(rpt);
+		rpt = next;
+	}
+	
+	/* Free the string and the report buffer. The check for NULL
+	 is necessary here as CFRelease() doesn't handle NULL like
+	 free() and others do. */
+	if (dev->run_loop_mode)
+		CFRelease(dev->run_loop_mode);
+	if (dev->source)
+		CFRelease(dev->source);
+	free(dev->input_report_buf);
+	
+	/* Clean up the thread objects */
+	pthread_barrier_destroy(&dev->shutdown_barrier);
+	pthread_barrier_destroy(&dev->barrier);
+	pthread_cond_destroy(&dev->condition);
+	pthread_mutex_destroy(&dev->mutex);
+	
+	/* Remove it from the device list. */
+	pthread_mutex_lock(&device_list_mutex);
+	hid_device *d = device_list;
+	if (d == dev) {
+		device_list = d->next;
+	}
+	else {
+		while (d) {
+			if (d->next == dev) {
+				d->next = d->next->next;
+				break;
+			}
+			
+			d = d->next;
+		}
+	}
+	pthread_mutex_unlock(&device_list_mutex);
+	
+	/* Free the structure itself. */
+	free(dev);
+}
+
+static 	IOHIDManagerRef hid_mgr = 0x0;
+
+
+#if 0
+static void register_error(hid_device *device, const char *op)
+{
+	
+}
+#endif
+
+
+static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
+{
+	CFTypeRef ref;
+	int32_t value;
+	
+	ref = IOHIDDeviceGetProperty(device, key);
+	if (ref) {
+		if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
+			CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
+			return value;
+		}
+	}
+	return 0;
+}
+
+static unsigned short get_vendor_id(IOHIDDeviceRef device)
+{
+	return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
+}
+
+static unsigned short get_product_id(IOHIDDeviceRef device)
+{
+	return get_int_property(device, CFSTR(kIOHIDProductIDKey));
+}
+
+
+static int32_t get_max_report_length(IOHIDDeviceRef device)
+{
+	return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
+}
+
+static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len)
+{
+	CFStringRef str;
+	
+	if (!len)
+		return 0;
+	
+	str = (CFStringRef)IOHIDDeviceGetProperty(device, prop);
+	
+	buf[0] = 0;
+	
+	if (str) {
+		len --;
+		
+		CFIndex str_len = CFStringGetLength(str);
+		CFRange range;
+		range.location = 0;
+		range.length = (str_len > len)? len: str_len;
+		CFIndex used_buf_len;
+		CFIndex chars_copied;
+		chars_copied = CFStringGetBytes(str,
+										range,
+										kCFStringEncodingUTF32LE,
+										(char)'?',
+										FALSE,
+										(UInt8*)buf,
+										len,
+										&used_buf_len);
+		
+		buf[chars_copied] = 0;
+		return (int)chars_copied;
+	}
+	else
+		return 0;
+	
+}
+
+static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len)
+{
+	CFStringRef str;
+	if (!len)
+		return 0;
+	
+	str = (CFStringRef)IOHIDDeviceGetProperty(device, prop);
+	
+	buf[0] = 0;
+	
+	if (str) {
+		len--;
+		
+		CFIndex str_len = CFStringGetLength(str);
+		CFRange range;
+		range.location = 0;
+		range.length = (str_len > len)? len: str_len;
+		CFIndex used_buf_len;
+		CFIndex chars_copied;
+		chars_copied = CFStringGetBytes(str,
+										range,
+										kCFStringEncodingUTF8,
+										(char)'?',
+										FALSE,
+										(UInt8*)buf,
+										len,
+										&used_buf_len);
+		
+		buf[chars_copied] = 0;
+		return (int)used_buf_len;
+	}
+	else
+		return 0;
+}
+
+
+static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len)
+{
+	return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
+}
+
+static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
+{
+	return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len);
+}
+
+static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
+{
+	return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len);
+}
+
+
+/* Implementation of wcsdup() for Mac. */
+static wchar_t *dup_wcs(const wchar_t *s)
+{
+	size_t len = wcslen(s);
+	wchar_t *ret = (wchar_t *)malloc((len+1)*sizeof(wchar_t));
+	wcscpy(ret, s);
+	
+	return ret;
+}
+
+
+static int make_path(IOHIDDeviceRef device, char *buf, size_t len)
+{
+	int res;
+	unsigned short vid, pid;
+	char transport[32];
+	
+	buf[0] = '\0';
+	
+	res = get_string_property_utf8(
+								   device, CFSTR(kIOHIDTransportKey),
+								   transport, sizeof(transport));
+	
+	if (!res)
+		return -1;
+	
+	vid = get_vendor_id(device);
+	pid = get_product_id(device);
+	
+	res = snprintf(buf, len, "%s_%04hx_%04hx_%p",
+				   transport, vid, pid, device);
+	
+	
+	buf[len-1] = '\0';
+	return res+1;
+}
+
+/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
+static int init_hid_manager(void)
+{
+
+	/* Initialize all the HID Manager Objects */
+	hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+	if (hid_mgr) {
+		IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
+		IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+		return 0;
+	}
+	
+	return -1;
+}
+
+/* Initialize the IOHIDManager if necessary. This is the public function, and
+ it is safe to call this function repeatedly. Return 0 for success and -1
+ for failure. */
+int HID_API_EXPORT hid_init(void)
+{
+	if (!hid_mgr) {
+		return init_hid_manager();
+	}
+	
+	/* Already initialized. */
+	return 0;
+}
+
+int HID_API_EXPORT hid_exit(void)
+{
+	if (hid_mgr) {
+		/* Close the HID manager. */
+		IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
+		CFRelease(hid_mgr);
+		hid_mgr = NULL;
+	}
+	
+	return 0;
+}
+
+static void process_pending_events() {
+	SInt32 res;
+	do {
+		res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
+	} while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
+}
+
+struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+	struct hid_device_info *root = NULL; // return object
+	struct hid_device_info *cur_dev = NULL;
+	CFIndex num_devices;
+	int i;
+	
+	setlocale(LC_ALL,"");
+	
+	/* Set up the HID Manager if it hasn't been done */
+	if (hid_init() < 0)
+		return NULL;
+	
+	/* give the IOHIDManager a chance to update itself */
+	process_pending_events();
+	
+	/* Get a list of the Devices */
+	CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
+	if (!device_set)
+		return NULL;
+
+	/* Convert the list into a C array so we can iterate easily. */	
+	num_devices = CFSetGetCount(device_set);
+	if (!num_devices) {
+		CFRelease(device_set);
+		return NULL;
+	}
+	IOHIDDeviceRef *device_array = (IOHIDDeviceRef*)calloc(num_devices, sizeof(IOHIDDeviceRef));
+	CFSetGetValues(device_set, (const void **) device_array);
+	
+	/* Iterate over each device, making an entry for it. */	
+	for (i = 0; i < num_devices; i++) {
+		unsigned short dev_vid;
+		unsigned short dev_pid;
+#define BUF_LEN 256
+		wchar_t buf[BUF_LEN];
+		char cbuf[BUF_LEN];
+		
+		IOHIDDeviceRef dev = device_array[i];
+		
+        if (!dev) {
+            continue;
+        }
+		dev_vid = get_vendor_id(dev);
+		dev_pid = get_product_id(dev);
+		
+		/* Check the VID/PID against the arguments */
+		if ((vendor_id == 0x0 && product_id == 0x0) ||
+		    (vendor_id == dev_vid && product_id == dev_pid)) {
+			struct hid_device_info *tmp;
+			size_t len;
+			
+			/* VID/PID match. Create the record. */
+			tmp = (struct hid_device_info *)malloc(sizeof(struct hid_device_info));
+			if (cur_dev) {
+				cur_dev->next = tmp;
+			}
+			else {
+				root = tmp;
+			}
+			cur_dev = tmp;
+			
+			// Get the Usage Page and Usage for this device.
+			cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey));
+			cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey));
+			
+			/* Fill out the record */
+			cur_dev->next = NULL;
+			len = make_path(dev, cbuf, sizeof(cbuf));
+			cur_dev->path = strdup(cbuf);
+			
+			/* Serial Number */
+			get_serial_number(dev, buf, BUF_LEN);
+			cur_dev->serial_number = dup_wcs(buf);
+			
+			/* Manufacturer and Product strings */
+			get_manufacturer_string(dev, buf, BUF_LEN);
+			cur_dev->manufacturer_string = dup_wcs(buf);
+			get_product_string(dev, buf, BUF_LEN);
+			cur_dev->product_string = dup_wcs(buf);
+			
+			/* VID/PID */
+			cur_dev->vendor_id = dev_vid;
+			cur_dev->product_id = dev_pid;
+			
+			/* Release Number */
+			cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
+			
+			/* Interface Number (Unsupported on Mac)*/
+			cur_dev->interface_number = -1;
+		}
+	}
+	
+	free(device_array);
+	CFRelease(device_set);
+	
+	return root;
+}
+
+void  HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
+{
+	/* This function is identical to the Linux version. Platform independent. */
+	struct hid_device_info *d = devs;
+	while (d) {
+		struct hid_device_info *next = d->next;
+		free(d->path);
+		free(d->serial_number);
+		free(d->manufacturer_string);
+		free(d->product_string);
+		free(d);
+		d = next;
+	}
+}
+
+hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
+{
+	/* This function is identical to the Linux version. Platform independent. */
+	struct hid_device_info *devs, *cur_dev;
+	const char *path_to_open = NULL;
+	hid_device * handle = NULL;
+	
+	devs = hid_enumerate(vendor_id, product_id);
+	cur_dev = devs;
+	while (cur_dev) {
+		if (cur_dev->vendor_id == vendor_id &&
+		    cur_dev->product_id == product_id) {
+			if (serial_number) {
+				if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
+					path_to_open = cur_dev->path;
+					break;
+				}
+			}
+			else {
+				path_to_open = cur_dev->path;
+				break;
+			}
+		}
+		cur_dev = cur_dev->next;
+	}
+	
+	if (path_to_open) {
+		/* Open the device */
+		handle = hid_open_path(path_to_open, 0);
+	}
+	
+	hid_free_enumeration(devs);
+	
+	return handle;
+}
+
+static void hid_device_removal_callback(void *context, IOReturn result,
+                                        void *sender, IOHIDDeviceRef dev_ref)
+{
+	/* Stop the Run Loop for this device. */
+	pthread_mutex_lock(&device_list_mutex);
+	hid_device *d = device_list;
+	while (d) {
+		if (d->device_handle == dev_ref) {
+			d->disconnected = 1;
+			CFRunLoopStop(d->run_loop);
+		}
+		
+		d = d->next;
+	}
+	pthread_mutex_unlock(&device_list_mutex);
+}
+
+/* The Run Loop calls this function for each input report received.
+ This function puts the data into a linked list to be picked up by
+ hid_read(). */
+static void hid_report_callback(void *context, IOReturn result, void *sender,
+								IOHIDReportType report_type, uint32_t report_id,
+								uint8_t *report, CFIndex report_length)
+{
+	struct input_report *rpt;
+	hid_device *dev = (hid_device *)context;
+	
+	/* Make a new Input Report object */
+	rpt = (struct input_report *)calloc(1, sizeof(struct input_report));
+	rpt->data = (uint8_t *)calloc(1, report_length);
+	memcpy(rpt->data, report, report_length);
+	rpt->len = report_length;
+	rpt->next = NULL;
+	
+	/* Lock this section */
+	pthread_mutex_lock(&dev->mutex);
+	
+	/* Attach the new report object to the end of the list. */
+	if (dev->input_reports == NULL) {
+		/* The list is empty. Put it at the root. */
+		dev->input_reports = rpt;
+	}
+	else {
+		/* Find the end of the list and attach. */
+		struct input_report *cur = dev->input_reports;
+		int num_queued = 0;
+		while (cur->next != NULL) {
+			cur = cur->next;
+			num_queued++;
+		}
+		cur->next = rpt;
+		
+		/* Pop one off if we've reached 30 in the queue. This
+		 way we don't grow forever if the user never reads
+		 anything from the device. */
+		if (num_queued > 30) {
+			return_data(dev, NULL, 0);
+		}
+	}
+	
+	/* Signal a waiting thread that there is data. */
+	pthread_cond_signal(&dev->condition);
+	
+	/* Unlock */
+	pthread_mutex_unlock(&dev->mutex);
+	
+}
+
+/* This gets called when the read_thred's run loop gets signaled by
+ hid_close(), and serves to stop the read_thread's run loop. */
+static void perform_signal_callback(void *context)
+{
+	hid_device *dev = (hid_device *)context;
+	CFRunLoopStop(dev->run_loop); //TODO: CFRunLoopGetCurrent()
+}
+
+static void *read_thread(void *param)
+{
+	hid_device *dev = (hid_device *)param;
+	
+	/* Move the device's run loop to this thread. */
+	IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
+	
+	/* Create the RunLoopSource which is used to signal the
+	 event loop to stop when hid_close() is called. */
+	CFRunLoopSourceContext ctx;
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.version = 0;
+	ctx.info = dev;
+	ctx.perform = &perform_signal_callback;
+	dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
+	CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
+	
+	/* Store off the Run Loop so it can be stopped from hid_close()
+	 and on device disconnection. */
+	dev->run_loop = CFRunLoopGetCurrent();
+	
+	/* Notify the main thread that the read thread is up and running. */
+	pthread_barrier_wait(&dev->barrier);
+	
+	/* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
+	 reports into the hid_report_callback(). */
+	SInt32 code;
+	while (!dev->shutdown_thread && !dev->disconnected) {
+		code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
+		/* Return if the device has been disconnected */
+		if (code == kCFRunLoopRunFinished) {
+			dev->disconnected = 1;
+			break;
+		}
+		
+		
+		/* Break if The Run Loop returns Finished or Stopped. */
+		if (code != kCFRunLoopRunTimedOut &&
+		    code != kCFRunLoopRunHandledSource) {
+			/* There was some kind of error. Setting
+			 shutdown seems to make sense, but
+			 there may be something else more appropriate */
+			dev->shutdown_thread = 1;
+			break;
+		}
+	}
+	
+	/* Now that the read thread is stopping, Wake any threads which are
+	 waiting on data (in hid_read_timeout()). Do this under a mutex to
+	 make sure that a thread which is about to go to sleep waiting on
+	 the condition acutally will go to sleep before the condition is
+	 signaled. */
+	pthread_mutex_lock(&dev->mutex);
+	pthread_cond_broadcast(&dev->condition);
+	pthread_mutex_unlock(&dev->mutex);
+	
+	/* Wait here until hid_close() is called and makes it past
+	 the call to CFRunLoopWakeUp(). This thread still needs to
+	 be valid when that function is called on the other thread. */
+	pthread_barrier_wait(&dev->shutdown_barrier);
+	
+	return NULL;
+}
+
+hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
+{
+  	int i;
+	hid_device *dev = NULL;
+	CFIndex num_devices;
+	
+	dev = new_hid_device();
+	
+	/* Set up the HID Manager if it hasn't been done */
+	if (hid_init() < 0)
+		return NULL;
+	
+	/* give the IOHIDManager a chance to update itself */
+	process_pending_events();
+	
+	CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
+	
+	num_devices = CFSetGetCount(device_set);
+	IOHIDDeviceRef *device_array = (IOHIDDeviceRef *)calloc(num_devices, sizeof(IOHIDDeviceRef));
+	CFSetGetValues(device_set, (const void **) device_array);	
+	for (i = 0; i < num_devices; i++) {
+		char cbuf[BUF_LEN];
+		size_t len;
+		IOHIDDeviceRef os_dev = device_array[i];
+		
+		len = make_path(os_dev, cbuf, sizeof(cbuf));
+		if (!strcmp(cbuf, path)) {
+			// Matched Paths. Open this Device.
+			IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeNone);
+			if (ret == kIOReturnSuccess) {
+				char str[32];
+				
+				free(device_array);
+				CFRelease(device_set);
+				dev->device_handle = os_dev;
+				
+				/* Create the buffers for receiving data */
+				dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev);
+				dev->input_report_buf = (uint8_t *)calloc(dev->max_input_report_len, sizeof(uint8_t));
+				
+				/* Create the Run Loop Mode for this device.
+				 printing the reference seems to work. */
+				sprintf(str, "HIDAPI_%p", os_dev);
+				dev->run_loop_mode = 
+				CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
+				
+				/* Attach the device to a Run Loop */
+				IOHIDDeviceRegisterInputReportCallback(
+													   os_dev, dev->input_report_buf, dev->max_input_report_len,
+													   &hid_report_callback, dev);
+				IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL);
+				
+				/* Start the read thread */
+				pthread_create(&dev->thread, NULL, read_thread, dev);
+				
+				/* Wait here for the read thread to be initialized. */
+				pthread_barrier_wait(&dev->barrier);
+				
+				return dev;
+			}
+			else {
+				goto return_error;
+			}
+		}
+	}
+	
+return_error:
+	free(device_array);
+	CFRelease(device_set);
+	free_hid_device(dev);
+	return NULL;
+}
+
+static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
+{
+	const char *pass_through_magic = "MAGIC0";
+	size_t pass_through_magic_length = strlen(pass_through_magic);
+	unsigned char report_id = data[0];
+	const unsigned char *data_to_send;
+	size_t length_to_send;
+	IOReturn res;
+	
+	/* Return if the device has been disconnected. */
+   	if (dev->disconnected)
+   		return -1;
+	
+	if (report_id == 0x0) {
+		/* Not using numbered Reports.
+		 Don't send the report number. */
+		data_to_send = data+1;
+		length_to_send = length-1;
+	}
+	else if (length > 6 && memcmp(data, pass_through_magic, pass_through_magic_length) == 0) {
+		report_id = data[pass_through_magic_length];
+		data_to_send = data+pass_through_magic_length;
+		length_to_send = length-pass_through_magic_length;
+	}
+	else {
+		/* Using numbered Reports.
+		 Send the Report Number */
+		data_to_send = data;
+		length_to_send = length;
+	}
+	
+	if (!dev->disconnected) {
+		res = IOHIDDeviceSetReport(dev->device_handle,
+								   type,
+								   report_id, /* Report ID*/
+								   data_to_send, length_to_send);
+		
+		if (res == kIOReturnSuccess) {
+			return (int)length;
+		}
+		else if (res == kIOReturnUnsupported) {
+			/*printf("kIOReturnUnsupported\n");*/
+			return -1;
+		}
+		else {
+			/*printf("0x%x\n", res);*/
+			return -1;
+		}
+	}
+	
+	return -1;
+}
+
+int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
+{
+	return set_report(dev, kIOHIDReportTypeOutput, data, length);
+}
+
+/* Helper function, so that this isn't duplicated in hid_read(). */
+static int return_data(hid_device *dev, unsigned char *data, size_t length)
+{
+	/* Copy the data out of the linked list item (rpt) into the
+	 return buffer (data), and delete the liked list item. */
+	struct input_report *rpt = dev->input_reports;
+	size_t len = (length < rpt->len)? length: rpt->len;
+	memcpy(data, rpt->data, len);
+	dev->input_reports = rpt->next;
+	free(rpt->data);
+	free(rpt);
+	return (int)len;
+}
+
+static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+	while (!dev->input_reports) {
+		int res = pthread_cond_wait(cond, mutex);
+		if (res != 0)
+			return res;
+		
+		/* A res of 0 means we may have been signaled or it may
+		 be a spurious wakeup. Check to see that there's acutally
+		 data in the queue before returning, and if not, go back
+		 to sleep. See the pthread_cond_timedwait() man page for
+		 details. */
+		
+		if (dev->shutdown_thread || dev->disconnected)
+			return -1;
+	}
+	
+	return 0;
+}
+
+static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+	while (!dev->input_reports) {
+		int res = pthread_cond_timedwait(cond, mutex, abstime);
+		if (res != 0)
+			return res;
+		
+		/* A res of 0 means we may have been signaled or it may
+		 be a spurious wakeup. Check to see that there's acutally
+		 data in the queue before returning, and if not, go back
+		 to sleep. See the pthread_cond_timedwait() man page for
+		 details. */
+		
+		if (dev->shutdown_thread || dev->disconnected)
+			return -1;
+	}
+	
+	return 0;
+	
+}
+
+int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
+{
+	int bytes_read = -1;
+	
+	/* Lock the access to the report list. */
+	pthread_mutex_lock(&dev->mutex);
+	
+	/* There's an input report queued up. Return it. */
+	if (dev->input_reports) {
+		/* Return the first one */
+		bytes_read = return_data(dev, data, length);
+		goto ret;
+	}
+	
+	/* Return if the device has been disconnected. */
+	if (dev->disconnected) {
+		bytes_read = -1;
+		goto ret;
+	}
+	
+	if (dev->shutdown_thread) {
+		/* This means the device has been closed (or there
+		 has been an error. An error code of -1 should
+		 be returned. */
+		bytes_read = -1;
+		goto ret;
+	}
+	
+	/* There is no data. Go to sleep and wait for data. */
+	
+	if (milliseconds == -1) {
+		/* Blocking */
+		int res;
+		res = cond_wait(dev, &dev->condition, &dev->mutex);
+		if (res == 0)
+			bytes_read = return_data(dev, data, length);
+		else {
+			/* There was an error, or a device disconnection. */
+			bytes_read = -1;
+		}
+	}
+	else if (milliseconds > 0) {
+		/* Non-blocking, but called with timeout. */
+		int res;
+		struct timespec ts;
+		struct timeval tv;
+		gettimeofday(&tv, NULL);
+		TIMEVAL_TO_TIMESPEC(&tv, &ts);
+		ts.tv_sec += milliseconds / 1000;
+		ts.tv_nsec += (milliseconds % 1000) * 1000000;
+		if (ts.tv_nsec >= 1000000000L) {
+			ts.tv_sec++;
+			ts.tv_nsec -= 1000000000L;
+		}
+		
+		res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
+		if (res == 0)
+			bytes_read = return_data(dev, data, length);
+		else if (res == ETIMEDOUT)
+			bytes_read = 0;
+		else
+			bytes_read = -1;
+	}
+	else {
+		/* Purely non-blocking */
+		bytes_read = 0;
+	}
+	
+ret:
+	/* Unlock */
+	pthread_mutex_unlock(&dev->mutex);
+	return bytes_read;
+}
+
+int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
+{
+	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
+}
+
+int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
+{
+	/* All Nonblocking operation is handled by the library. */
+	dev->blocking = !nonblock;
+	
+	return 0;
+}
+
+int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
+{
+	return set_report(dev, kIOHIDReportTypeFeature, data, length);
+}
+
+int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+{
+	CFIndex len = length;
+	IOReturn res;
+	
+	/* Return if the device has been unplugged. */
+	if (dev->disconnected)
+		return -1;
+	
+	int skipped_report_id = 0;
+	int report_number = data[0];
+	if (report_number == 0x0) {
+		/* Offset the return buffer by 1, so that the report ID
+		 will remain in byte 0. */
+		data++;
+		len--;
+		skipped_report_id = 1;
+	}
+	
+	res = IOHIDDeviceGetReport(dev->device_handle,
+	                           kIOHIDReportTypeFeature,
+	                           report_number, /* Report ID */
+	                           data, &len);
+	if (res != kIOReturnSuccess)
+		return -1;
+
+	if (skipped_report_id)
+		len++;
+
+	return (int)len;
+}
+
+
+void HID_API_EXPORT hid_close(hid_device *dev)
+{
+	if (!dev)
+		return;
+	
+	/* Disconnect the report callback before close. */
+	if (!dev->disconnected) {
+		IOHIDDeviceRegisterInputReportCallback(
+											   dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
+											   NULL, dev);
+		IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, NULL, dev);
+		IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
+		IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+	}
+	
+	/* Cause read_thread() to stop. */
+	dev->shutdown_thread = 1;
+	
+	/* Wake up the run thread's event loop so that the thread can exit. */
+	CFRunLoopSourceSignal(dev->source);
+	CFRunLoopWakeUp(dev->run_loop);
+	
+	/* Notify the read thread that it can shut down now. */
+	pthread_barrier_wait(&dev->shutdown_barrier);
+	
+	/* Wait for read_thread() to end. */
+	pthread_join(dev->thread, NULL);
+	
+	/* Close the OS handle to the device, but only if it's not
+	 been unplugged. If it's been unplugged, then calling
+	 IOHIDDeviceClose() will crash. */
+	if (!dev->disconnected) {
+		IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone);
+	}
+	
+	/* Clear out the queue of received reports. */
+	pthread_mutex_lock(&dev->mutex);
+	while (dev->input_reports) {
+		return_data(dev, NULL, 0);
+	}
+	pthread_mutex_unlock(&dev->mutex);
+	
+	free_hid_device(dev);
+}
+
+int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return get_manufacturer_string(dev->device_handle, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return get_product_string(dev->device_handle, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	return get_serial_number(dev->device_handle, string, maxlen);
+}
+
+int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
+{
+	// TODO:
+	
+	return 0;
+}
+
+
+HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
+{
+	// TODO:
+	
+	return NULL;
+}
+
+
+
+
+
+
+#if 0
+static int32_t get_location_id(IOHIDDeviceRef device)
+{
+	return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
+}
+
+static int32_t get_usage(IOHIDDeviceRef device)
+{
+	int32_t res;
+	res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey));
+	if (!res)
+		res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
+	return res;
+}
+
+static int32_t get_usage_page(IOHIDDeviceRef device)
+{
+	int32_t res;
+	res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey));
+	if (!res)
+		res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
+	return res;
+}
+
+static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len)
+{
+	return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len);
+}
+
+
+int main(void)
+{
+	IOHIDManagerRef mgr;
+	int i;
+	
+	mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+	IOHIDManagerSetDeviceMatching(mgr, NULL);
+	IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone);
+	
+	CFSetRef device_set = IOHIDManagerCopyDevices(mgr);
+	
+	CFIndex num_devices = CFSetGetCount(device_set);
+	IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
+	CFSetGetValues(device_set, (const void **) device_array);
+	
+	setlocale(LC_ALL, "");
+	
+	for (i = 0; i < num_devices; i++) {
+		IOHIDDeviceRef dev = device_array[i];
+		printf("Device: %p\n", dev);
+		printf("  %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev));
+		
+		wchar_t serial[256], buf[256];
+		char cbuf[256];
+		get_serial_number(dev, serial, 256);
+		
+		
+		printf("  Serial: %ls\n", serial);
+		printf("  Loc: %ld\n", get_location_id(dev));
+		get_transport(dev, buf, 256);
+		printf("  Trans: %ls\n", buf);
+		make_path(dev, cbuf, 256);
+		printf("  Path: %s\n", cbuf);
+		
+	}
+	
+	return 0;
+}
+#endif
+
+#endif /* SDL_JOYSTICK_HIDAPI */
diff --git a/source/src/hidapi/pc/hidapi-hidraw.pc.in b/source/src/hidapi/pc/hidapi-hidraw.pc.in
new file mode 100644
index 0000000..e20558d
--- /dev/null
+++ b/source/src/hidapi/pc/hidapi-hidraw.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: hidapi-hidraw
+Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the hidraw implementation.
+Version: @VERSION@
+Libs: -L${libdir} -lhidapi-hidraw
+Cflags: -I${includedir}/hidapi
diff --git a/source/src/hidapi/pc/hidapi-libusb.pc.in b/source/src/hidapi/pc/hidapi-libusb.pc.in
new file mode 100644
index 0000000..2e49506
--- /dev/null
+++ b/source/src/hidapi/pc/hidapi-libusb.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: hidapi-libusb
+Description: C Library for USB HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the libusb implementation.
+Version: @VERSION@
+Libs: -L${libdir} -lhidapi-libusb
+Cflags: -I${includedir}/hidapi
diff --git a/source/src/hidapi/pc/hidapi.pc.in b/source/src/hidapi/pc/hidapi.pc.in
new file mode 100644
index 0000000..5835c99
--- /dev/null
+++ b/source/src/hidapi/pc/hidapi.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: hidapi
+Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows.
+Version: @VERSION@
+Libs: -L${libdir} -lhidapi
+Cflags: -I${includedir}/hidapi
diff --git a/source/src/hidapi/testgui/Makefile-manual b/source/src/hidapi/testgui/Makefile-manual
new file mode 100644
index 0000000..3f61705
--- /dev/null
+++ b/source/src/hidapi/testgui/Makefile-manual
@@ -0,0 +1,26 @@
+
+
+OS=$(shell uname)
+
+ifeq ($(OS), Darwin)
+	FILE=Makefile.mac
+endif
+
+ifneq (,$(findstring MINGW,$(OS)))
+	FILE=Makefile.mingw
+endif
+
+ifeq ($(OS), Linux)
+	FILE=Makefile.linux
+endif
+
+ifeq ($(OS), FreeBSD)
+	FILE=Makefile.freebsd
+endif
+
+ifeq ($(FILE), )
+all:
+	$(error Your platform ${OS} is not supported at this time.)
+endif
+
+include $(FILE)
diff --git a/source/src/hidapi/testgui/Makefile.am b/source/src/hidapi/testgui/Makefile.am
new file mode 100644
index 0000000..1c02f3f
--- /dev/null
+++ b/source/src/hidapi/testgui/Makefile.am
@@ -0,0 +1,43 @@
+
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi/ $(CFLAGS_TESTGUI)
+
+if OS_LINUX
+## Linux
+bin_PROGRAMS = hidapi-hidraw-testgui hidapi-libusb-testgui
+
+hidapi_hidraw_testgui_SOURCES = test.cpp
+hidapi_hidraw_testgui_LDADD = $(top_builddir)/linux/libhidapi-hidraw.la $(LIBS_TESTGUI)
+
+hidapi_libusb_testgui_SOURCES = test.cpp
+hidapi_libusb_testgui_LDADD = $(top_builddir)/libusb/libhidapi-libusb.la $(LIBS_TESTGUI)
+else
+## Other OS's
+bin_PROGRAMS = hidapi-testgui
+
+hidapi_testgui_SOURCES = test.cpp
+hidapi_testgui_LDADD = $(top_builddir)/$(backend)/libhidapi.la $(LIBS_TESTGUI)
+endif
+
+if OS_DARWIN
+hidapi_testgui_SOURCES = test.cpp mac_support_cocoa.m mac_support.h
+# Rules for copying the binary and its dependencies into the app bundle.
+TestGUI.app/Contents/MacOS/hidapi-testgui$(EXEEXT): hidapi-testgui$(EXEEXT)
+	$(srcdir)/copy_to_bundle.sh
+
+all: all-am TestGUI.app/Contents/MacOS/hidapi-testgui$(EXEEXT)
+
+endif
+
+EXTRA_DIST = \
+ copy_to_bundle.sh \
+ Makefile-manual \
+ Makefile.freebsd \
+ Makefile.linux \
+ Makefile.mac \
+ Makefile.mingw \
+ TestGUI.app.in \
+ testgui.sln \
+ testgui.vcproj
+
+distclean-local:
+	rm -rf TestGUI.app
diff --git a/source/src/hidapi/testgui/Makefile.freebsd b/source/src/hidapi/testgui/Makefile.freebsd
new file mode 100644
index 0000000..09a2473
--- /dev/null
+++ b/source/src/hidapi/testgui/Makefile.freebsd
@@ -0,0 +1,33 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: testgui
+
+CC=cc
+CXX=c++
+COBJS=../libusb/hid.o
+CPPOBJS=test.o
+OBJS=$(COBJS) $(CPPOBJS)
+CFLAGS=-I../hidapi -I/usr/local/include `fox-config --cflags` -Wall -g -c
+LDFLAGS= -L/usr/local/lib
+LIBS= -lusb -liconv `fox-config --libs` -pthread
+
+
+testgui: $(OBJS)
+	$(CXX) -Wall -g $^ $(LDFLAGS) -o $@ $(LIBS)
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+clean:
+	rm *.o testgui
+
+.PHONY: clean
diff --git a/source/src/hidapi/testgui/Makefile.linux b/source/src/hidapi/testgui/Makefile.linux
new file mode 100644
index 0000000..d32e163
--- /dev/null
+++ b/source/src/hidapi/testgui/Makefile.linux
@@ -0,0 +1,32 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: testgui
+
+CC=gcc
+CXX=g++
+COBJS=../libusb/hid.o
+CPPOBJS=test.o
+OBJS=$(COBJS) $(CPPOBJS)
+CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags` `pkg-config libusb-1.0 --cflags`
+LIBS=-ludev -lrt -lpthread `fox-config --libs` `pkg-config libusb-1.0 --libs`
+
+
+testgui: $(OBJS)
+	g++ -Wall -g $^ $(LIBS) -o testgui
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+clean:
+	rm *.o testgui
+
+.PHONY: clean
diff --git a/source/src/hidapi/testgui/Makefile.mac b/source/src/hidapi/testgui/Makefile.mac
new file mode 100644
index 0000000..cda7d49
--- /dev/null
+++ b/source/src/hidapi/testgui/Makefile.mac
@@ -0,0 +1,46 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-07-03
+###########################################
+
+all: hidapi-testgui
+
+CC=gcc
+CXX=g++
+COBJS=../mac/hid.o
+CPPOBJS=test.o
+OBJCOBJS=mac_support_cocoa.o
+OBJS=$(COBJS) $(CPPOBJS) $(OBJCOBJS)
+CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags`
+LDFLAGS=-L/usr/X11R6/lib
+LIBS=`fox-config --libs` -framework IOKit -framework CoreFoundation -framework Cocoa
+
+
+hidapi-testgui: $(OBJS) TestGUI.app
+	g++ -Wall -g $(OBJS) $(LIBS) $(LDFLAGS) -o hidapi-testgui
+	./copy_to_bundle.sh
+	#cp TestGUI.app/Contents/MacOS/hidapi-testgui  TestGUI.app/Contents/MacOS/tg
+	#cp start.sh TestGUI.app/Contents/MacOS/hidapi-testgui
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+$(OBJCOBJS): %.o: %.m
+	$(CXX) $(CFLAGS) -x objective-c++ $< -o $@
+
+TestGUI.app: TestGUI.app.in
+	rm -Rf TestGUI.app
+	mkdir -p TestGUI.app
+	cp -R TestGUI.app.in/ TestGUI.app
+
+clean:
+	rm -f $(OBJS) hidapi-testgui
+	rm -Rf TestGUI.app
+
+.PHONY: clean
diff --git a/source/src/hidapi/testgui/Makefile.mingw b/source/src/hidapi/testgui/Makefile.mingw
new file mode 100644
index 0000000..df0f69d
--- /dev/null
+++ b/source/src/hidapi/testgui/Makefile.mingw
@@ -0,0 +1,32 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidapi-testgui
+
+CC=gcc
+CXX=g++
+COBJS=../windows/hid.o
+CPPOBJS=test.o
+OBJS=$(COBJS) $(CPPOBJS)
+CFLAGS=-I../hidapi -I../../hidapi-externals/fox/include -g -c
+LIBS= -mwindows -lsetupapi -L../../hidapi-externals/fox/lib -Wl,-Bstatic -lFOX-1.6 -Wl,-Bdynamic -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32
+
+
+hidapi-testgui: $(OBJS)
+	g++ -g $^ $(LIBS) -o hidapi-testgui
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+clean:
+	rm -f *.o hidapi-testgui.exe
+
+.PHONY: clean
diff --git a/source/src/hidapi/testgui/TestGUI.app.in/Contents/Info.plist b/source/src/hidapi/testgui/TestGUI.app.in/Contents/Info.plist
new file mode 100644
index 0000000..ab473d5
--- /dev/null
+++ b/source/src/hidapi/testgui/TestGUI.app.in/Contents/Info.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string></string>
+	<key>CFBundleExecutable</key>
+	<string>hidapi-testgui</string>
+	<key>CFBundleIconFile</key>
+	<string>Signal11.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>us.signal11.hidtestgui</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>testgui</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>CSResourcesFileMapped</key>
+	<true/>
+</dict>
+</plist>
diff --git a/source/src/hidapi/testgui/TestGUI.app.in/Contents/PkgInfo b/source/src/hidapi/testgui/TestGUI.app.in/Contents/PkgInfo
new file mode 100644
index 0000000..bd04210
--- /dev/null
+++ b/source/src/hidapi/testgui/TestGUI.app.in/Contents/PkgInfo
@@ -0,0 +1 @@
+APPL????
\ No newline at end of file
diff --git a/source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings b/source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..dea12de
--- /dev/null
+++ b/source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns b/source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns
new file mode 100644
index 0000000..bb6b7bd
--- /dev/null
+++ b/source/src/hidapi/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns
Binary files differ
diff --git a/source/src/hidapi/testgui/copy_to_bundle.sh b/source/src/hidapi/testgui/copy_to_bundle.sh
new file mode 100755
index 0000000..f0fc767
--- /dev/null
+++ b/source/src/hidapi/testgui/copy_to_bundle.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+#### Configuration:
+# The name of the executable. It is assumed
+# that it is in the current working directory.
+EXE_NAME=hidapi-testgui
+# Path to the executable directory inside the bundle.
+# This must be an absolute path, so use $PWD.
+EXEPATH=$PWD/TestGUI.app/Contents/MacOS
+# Libraries to explicitly bundle, even though they
+# may not be in /opt/local. One per line. These
+# are used with grep, so only a portion of the name
+# is required. eg: libFOX, libz, etc.
+LIBS_TO_BUNDLE=libFOX
+
+
+function copydeps {
+	local file=$1
+	# echo "Copying deps for $file...."
+	local BASE_OF_EXE=`basename $file`
+
+	# A will contain the dependencies of this library
+	local A=`otool -LX $file |cut -f 1 -d " "`
+	local i
+	for i in $A; do
+		local BASE=`basename $i`
+
+		# See if it's a lib we specifically want to bundle
+		local bundle_this_lib=0
+		local j
+		for j in $LIBS_TO_BUNDLE; do
+			echo $i |grep -q $j
+			if [ $? -eq 0 ]; then
+				bundle_this_lib=1
+				echo "bundling $i because it's in the list."
+				break;
+			fi
+		done
+
+		# See if it's in /opt/local. Bundle all in /opt/local
+		local isOptLocal=0
+		echo $i |grep -q /opt/local
+		if [ $? -eq 0 ]; then
+			isOptLocal=1
+			echo "bundling $i because it's in /opt/local."
+		fi
+		
+		# Bundle the library
+		if [ $isOptLocal -ne 0 ] || [ $bundle_this_lib -ne 0 ]; then
+
+			# Copy the file into the bundle if it exists.
+			if [ -f $EXEPATH/$BASE ]; then
+				z=0
+			else
+				cp $i $EXEPATH
+				chmod 755 $EXEPATH/$BASE
+			fi
+			
+			
+			# echo "$BASE_OF_EXE depends on $BASE"
+			
+			# Fix the paths using install_name_tool and then
+			# call this function recursively for each dependency
+			# of this library. 
+			if [ $BASE_OF_EXE != $BASE ]; then
+			
+				# Fix the paths
+				install_name_tool -id @executable_path/$BASE $EXEPATH/$BASE
+				install_name_tool -change $i @executable_path/$BASE $EXEPATH/$BASE_OF_EXE
+
+				# Call this function (recursive) on
+				# on each dependency of this library.
+				copydeps $EXEPATH/$BASE
+			fi
+		fi
+	done
+}
+
+rm -f $EXEPATH/*
+
+# Copy the binary into the bundle. Use ../libtool to do this if it's
+# available beacuse if $EXE_NAME was built with autotools, it will be
+# necessary.  If ../libtool not available, just use cp to do the copy, but
+# only if $EXE_NAME is a binary.
+if [ -x ../libtool ]; then
+	../libtool --mode=install cp $EXE_NAME $EXEPATH
+else
+	file -bI $EXE_NAME |grep binary
+	if [ $? -ne 0 ]; then
+		echo "There is no ../libtool and $EXE_NAME is not a binary."
+		echo "I'm not sure what to do."
+		exit 1
+	else
+		cp $EXE_NAME $EXEPATH
+	fi
+fi
+copydeps $EXEPATH/$EXE_NAME
diff --git a/source/src/hidapi/testgui/mac_support.cpp b/source/src/hidapi/testgui/mac_support.cpp
new file mode 100644
index 0000000..e1e3874
--- /dev/null
+++ b/source/src/hidapi/testgui/mac_support.cpp
@@ -0,0 +1,134 @@
+/*******************************
+ Mac support for HID Test GUI
+ 
+ Alan Ott
+ Signal 11 Software
+
+ Some of this code is from Apple Documentation, most notably
+ http://developer.apple.com/legacy/mac/library/documentation/AppleScript/Conceptual/AppleEvents/AppleEvents.pdf 
+*******************************/
+
+#include <Carbon/Carbon.h>
+#include <fx.h>
+
+
+extern FXMainWindow *g_main_window;
+
+static pascal OSErr HandleQuitMessage(const AppleEvent *theAppleEvent, AppleEvent 
+									  *reply, long handlerRefcon) 
+{
+	puts("Quitting\n");
+	FXApp::instance()->exit();
+	return 0;
+}
+
+static pascal OSErr HandleReopenMessage(const AppleEvent *theAppleEvent, AppleEvent 
+									  *reply, long handlerRefcon) 
+{
+	puts("Showing");
+	g_main_window->show();
+	return 0;
+}
+
+static pascal OSErr HandleWildCardMessage(const AppleEvent *theAppleEvent, AppleEvent 
+									  *reply, long handlerRefcon) 
+{
+	puts("WildCard\n");
+	return 0;
+}
+
+OSStatus AEHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) 
+{ 
+    Boolean     release = false; 
+    EventRecord eventRecord; 
+    OSErr       ignoreErrForThisSample; 
+	
+    // Events of type kEventAppleEvent must be removed from the queue 
+    //  before being passed to AEProcessAppleEvent. 
+    if (IsEventInQueue(GetMainEventQueue(), inEvent)) 
+    { 
+        // RemoveEventFromQueue will release the event, which will 
+        //  destroy it if we don't retain it first. 
+        RetainEvent(inEvent); 
+        release = true; 
+        RemoveEventFromQueue(GetMainEventQueue(), inEvent); 
+    } 
+    // Convert the event ref to the type AEProcessAppleEvent expects. 
+    ConvertEventRefToEventRecord(inEvent, &eventRecord); 
+    ignoreErrForThisSample = AEProcessAppleEvent(&eventRecord); 
+    if (release) 
+        ReleaseEvent(inEvent); 
+    // This Carbon event has been handled, even if no AppleEvent handlers 
+    //  were installed for the Apple event. 
+    return noErr; 
+}
+
+static void HandleEvent(EventRecord *event) 
+{ 
+	//printf("What: %d message %x\n", event->what, event->message);
+	if (event->what == osEvt) {
+		if (((event->message >> 24) & 0xff) == suspendResumeMessage) {
+			if (event->message & resumeFlag) {
+				g_main_window->show();				
+			}
+		}
+	}
+
+#if 0
+    switch (event->what) 
+    { 
+        case mouseDown: 
+            //HandleMouseDown(event); 
+            break; 
+        case keyDown: 
+        case autoKey: 
+            //HandleKeyPress(event); 
+            break; 
+        case kHighLevelEvent: 
+			puts("Calling ProcessAppleEvent\n");
+            AEProcessAppleEvent(event); 
+            break; 
+    } 
+#endif
+} 
+
+void
+init_apple_message_system()
+{
+	OSErr err;
+	static const EventTypeSpec appleEvents[] = 
+	{
+		{ kEventClassAppleEvent, kEventAppleEvent }
+	};
+	
+	/* Install the handler for Apple Events */
+	InstallApplicationEventHandler(NewEventHandlerUPP(AEHandler), 
+	              GetEventTypeCount(appleEvents), appleEvents, 0, NULL); 
+
+	/* Install handlers for the individual Apple Events that come
+	   from the Dock icon: the Reopen (click), and the Quit messages. */
+	err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 
+	              NewAEEventHandlerUPP(HandleQuitMessage), 0, false);
+	err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, 
+	              NewAEEventHandlerUPP(HandleReopenMessage), 0, false);
+#if 0
+	// Left as an example of a wild card match.
+	err = AEInstallEventHandler(kCoreEventClass, typeWildCard, 
+	              NewAEEventHandlerUPP(HandleWildMessage), 0, false);
+#endif
+}
+
+void
+check_apple_events()
+{
+	RgnHandle       cursorRgn = NULL; 
+	Boolean         gotEvent=TRUE; 
+	EventRecord     event; 
+
+	while (gotEvent) { 
+		gotEvent = WaitNextEvent(everyEvent, &event, 0L/*timeout*/, cursorRgn); 
+		if (gotEvent) { 
+			HandleEvent(&event); 
+		} 
+	}
+}
diff --git a/source/src/hidapi/testgui/mac_support.h b/source/src/hidapi/testgui/mac_support.h
new file mode 100644
index 0000000..7d9ab49
--- /dev/null
+++ b/source/src/hidapi/testgui/mac_support.h
@@ -0,0 +1,17 @@
+/*******************************
+ Mac support for HID Test GUI
+ 
+ Alan Ott
+ Signal 11 Software
+ 
+*******************************/
+
+#ifndef MAC_SUPPORT_H__
+#define MAC_SUPPORT_H__
+
+extern "C" {
+	void init_apple_message_system();
+	void check_apple_events();
+}
+
+#endif
diff --git a/source/src/hidapi/testgui/mac_support_cocoa.m b/source/src/hidapi/testgui/mac_support_cocoa.m
new file mode 100644
index 0000000..75de7e9
--- /dev/null
+++ b/source/src/hidapi/testgui/mac_support_cocoa.m
@@ -0,0 +1,94 @@
+/*******************************
+ Mac support for HID Test GUI
+ 
+ Alan Ott
+ Signal 11 Software
+*******************************/
+
+#include <fx.h>
+#import <Cocoa/Cocoa.h>
+
+extern FXMainWindow *g_main_window;
+
+
+@interface MyAppDelegate : NSObject
+{
+} 
+@end
+
+@implementation MyAppDelegate
+- (void) applicationWillBecomeActive:(NSNotification*)notif
+{
+	printf("WillBecomeActive\n");
+	g_main_window->show();
+
+}
+
+- (void) applicationWillTerminate:(NSNotification*)notif
+{
+	/* Doesn't get called. Not sure why */
+	printf("WillTerminate\n");
+	FXApp::instance()->exit();
+}
+
+- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender
+{
+	/* Doesn't get called. Not sure why */
+	printf("ShouldTerminate\n");
+	return YES;
+}
+
+- (void) applicationWillHide:(NSNotification*)notif
+{
+	printf("WillHide\n");
+	g_main_window->hide();
+}
+
+- (void) handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
+{
+	printf("QuitEvent\n");
+	FXApp::instance()->exit();
+}
+
+@end
+
+extern "C" {
+
+void
+init_apple_message_system()
+{
+	static MyAppDelegate *d = [MyAppDelegate new];
+
+	[[NSApplication sharedApplication] setDelegate:d];
+
+	/* Register for Apple Events. */
+	/* This is from
+	   http://stackoverflow.com/questions/1768497/application-exit-event */
+	NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager];
+	[aem setEventHandler:d
+	     andSelector:@selector(handleQuitEvent:withReplyEvent:)
+	     forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
+}
+
+void
+check_apple_events()
+{
+	NSApplication *app = [NSApplication sharedApplication];
+
+	NSAutoreleasePool *pool = [NSAutoreleasePool new];
+	while (1) {
+		NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+		                        untilDate:nil
+                                        inMode:NSDefaultRunLoopMode
+                                        dequeue:YES];
+		if (event == NULL)
+			break;
+		else {
+			//printf("Event happened: Type: %d\n", event->_type);
+			[app sendEvent: event];
+		}
+	}
+	[pool release];
+}
+
+} /* extern "C" */
diff --git a/source/src/hidapi/testgui/start.sh b/source/src/hidapi/testgui/start.sh
new file mode 100755
index 0000000..980635d
--- /dev/null
+++ b/source/src/hidapi/testgui/start.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+xterm -e /Users/alan/work/hidapi/testgui/TestGUI.app/Contents/MacOS/tg
diff --git a/source/src/hidapi/testgui/test.cpp b/source/src/hidapi/testgui/test.cpp
new file mode 100644
index 0000000..538db79
--- /dev/null
+++ b/source/src/hidapi/testgui/test.cpp
@@ -0,0 +1,532 @@
+/*******************************************************
+ Demo Program for HIDAPI
+ 
+ Alan Ott
+ Signal 11 Software
+
+ 2010-07-20
+
+ Copyright 2010, All Rights Reserved
+ 
+ This contents of this file may be used by anyone
+ for any reason without any conditions and may be
+ used as a starting point for your own applications
+ which use HIDAPI.
+********************************************************/
+
+
+#include <fx.h>
+
+#include "hidapi.h"
+#include "mac_support.h"
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifdef _WIN32
+	// Thanks Microsoft, but I know how to use strncpy().
+	#pragma warning(disable:4996)
+#endif
+
+class MainWindow : public FXMainWindow {
+	FXDECLARE(MainWindow)
+	
+public:
+	enum {
+		ID_FIRST = FXMainWindow::ID_LAST,
+		ID_CONNECT,
+		ID_DISCONNECT,
+		ID_RESCAN,
+		ID_SEND_OUTPUT_REPORT,
+		ID_SEND_FEATURE_REPORT,
+		ID_GET_FEATURE_REPORT,
+		ID_CLEAR,
+		ID_TIMER,
+		ID_MAC_TIMER,
+		ID_LAST,
+	};
+	
+private:
+	FXList *device_list;
+	FXButton *connect_button;
+	FXButton *disconnect_button;
+	FXButton *rescan_button;
+	FXButton *output_button;
+	FXLabel *connected_label;
+	FXTextField *output_text;
+	FXTextField *output_len;
+	FXButton *feature_button;
+	FXButton *get_feature_button;
+	FXTextField *feature_text;
+	FXTextField *feature_len;
+	FXTextField *get_feature_text;
+	FXText *input_text;
+	FXFont *title_font;
+	
+	struct hid_device_info *devices;
+	hid_device *connected_device;
+	size_t getDataFromTextField(FXTextField *tf, char *buf, size_t len);
+	int getLengthFromTextField(FXTextField *tf);
+
+
+protected:
+	MainWindow() {};
+public:
+	MainWindow(FXApp *a);
+	~MainWindow();
+	virtual void create();
+	
+	long onConnect(FXObject *sender, FXSelector sel, void *ptr);
+	long onDisconnect(FXObject *sender, FXSelector sel, void *ptr);
+	long onRescan(FXObject *sender, FXSelector sel, void *ptr);
+	long onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr);
+	long onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr);
+	long onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr);
+	long onClear(FXObject *sender, FXSelector sel, void *ptr);
+	long onTimeout(FXObject *sender, FXSelector sel, void *ptr);
+	long onMacTimeout(FXObject *sender, FXSelector sel, void *ptr);
+};
+
+// FOX 1.7 changes the timeouts to all be nanoseconds.
+// Fox 1.6 had all timeouts as milliseconds.
+#if (FOX_MINOR >= 7)
+	const int timeout_scalar = 1000*1000;
+#else
+	const int timeout_scalar = 1;
+#endif
+
+FXMainWindow *g_main_window;
+
+
+FXDEFMAP(MainWindow) MainWindowMap [] = {
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CONNECT, MainWindow::onConnect ),
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_DISCONNECT, MainWindow::onDisconnect ),
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_RESCAN, MainWindow::onRescan ),
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_OUTPUT_REPORT, MainWindow::onSendOutputReport ),
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_SEND_FEATURE_REPORT, MainWindow::onSendFeatureReport ),
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_GET_FEATURE_REPORT, MainWindow::onGetFeatureReport ),
+	FXMAPFUNC(SEL_COMMAND, MainWindow::ID_CLEAR, MainWindow::onClear ),
+	FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_TIMER, MainWindow::onTimeout ),
+	FXMAPFUNC(SEL_TIMEOUT, MainWindow::ID_MAC_TIMER, MainWindow::onMacTimeout ),
+};
+
+FXIMPLEMENT(MainWindow, FXMainWindow, MainWindowMap, ARRAYNUMBER(MainWindowMap));
+
+MainWindow::MainWindow(FXApp *app)
+	: FXMainWindow(app, "HIDAPI Test Application", NULL, NULL, DECOR_ALL, 200,100, 425,700)
+{
+	devices = NULL;
+	connected_device = NULL;
+
+	FXVerticalFrame *vf = new FXVerticalFrame(this, LAYOUT_FILL_Y|LAYOUT_FILL_X);
+
+	FXLabel *label = new FXLabel(vf, "HIDAPI Test Tool");
+	title_font = new FXFont(getApp(), "Arial", 14, FXFont::Bold);
+	label->setFont(title_font);
+	
+	new FXLabel(vf,
+		"Select a device and press Connect.", NULL, JUSTIFY_LEFT);
+	new FXLabel(vf,
+		"Output data bytes can be entered in the Output section, \n"
+		"separated by space, comma or brackets. Data starting with 0x\n"
+		"is treated as hex. Data beginning with a 0 is treated as \n"
+		"octal. All other data is treated as decimal.", NULL, JUSTIFY_LEFT);
+	new FXLabel(vf,
+		"Data received from the device appears in the Input section.",
+		NULL, JUSTIFY_LEFT);
+	new FXLabel(vf,
+		"Optionally, a report length may be specified. Extra bytes are\n"
+		"padded with zeros. If no length is specified, the length is \n"
+		"inferred from the data.",
+		NULL, JUSTIFY_LEFT);
+	new FXLabel(vf, "");
+
+	// Device List and Connect/Disconnect buttons
+	FXHorizontalFrame *hf = new FXHorizontalFrame(vf, LAYOUT_FILL_X);
+	//device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0,0,300,200);
+	device_list = new FXList(new FXHorizontalFrame(hf,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0), NULL, 0, LISTBOX_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y, 0,0,300,200);
+	FXVerticalFrame *buttonVF = new FXVerticalFrame(hf);
+	connect_button = new FXButton(buttonVF, "Connect", NULL, this, ID_CONNECT, BUTTON_NORMAL|LAYOUT_FILL_X);
+	disconnect_button = new FXButton(buttonVF, "Disconnect", NULL, this, ID_DISCONNECT, BUTTON_NORMAL|LAYOUT_FILL_X);
+	disconnect_button->disable();
+	rescan_button = new FXButton(buttonVF, "Re-Scan devices", NULL, this, ID_RESCAN, BUTTON_NORMAL|LAYOUT_FILL_X);
+	new FXHorizontalFrame(buttonVF, 0, 0,0,0,0, 0,0,50,0);
+
+	connected_label = new FXLabel(vf, "Disconnected");
+	
+	new FXHorizontalFrame(vf);
+	
+	// Output Group Box
+	FXGroupBox *gb = new FXGroupBox(vf, "Output", FRAME_GROOVE|LAYOUT_FILL_X);
+	FXMatrix *matrix = new FXMatrix(gb, 3, MATRIX_BY_COLUMNS|LAYOUT_FILL_X);
+	new FXLabel(matrix, "Data");
+	new FXLabel(matrix, "Length");
+	new FXLabel(matrix, "");
+
+	//hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X);
+	output_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN);
+	output_text->setText("1 0x81 0");
+	output_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN);
+	output_button = new FXButton(matrix, "Send Output Report", NULL, this, ID_SEND_OUTPUT_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X);
+	output_button->disable();
+	//new FXHorizontalFrame(matrix, LAYOUT_FILL_X);
+
+	//hf = new FXHorizontalFrame(gb, LAYOUT_FILL_X);
+	feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN);
+	feature_len = new FXTextField(matrix, 5, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN);
+	feature_button = new FXButton(matrix, "Send Feature Report", NULL, this, ID_SEND_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X);
+	feature_button->disable();
+
+	get_feature_text = new FXTextField(matrix, 30, NULL, 0, TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN);
+	new FXWindow(matrix);
+	get_feature_button = new FXButton(matrix, "Get Feature Report", NULL, this, ID_GET_FEATURE_REPORT, BUTTON_NORMAL|LAYOUT_FILL_X);
+	get_feature_button->disable();
+
+
+	// Input Group Box
+	gb = new FXGroupBox(vf, "Input", FRAME_GROOVE|LAYOUT_FILL_X|LAYOUT_FILL_Y);
+	FXVerticalFrame *innerVF = new FXVerticalFrame(gb, LAYOUT_FILL_X|LAYOUT_FILL_Y);
+	input_text = new FXText(new FXHorizontalFrame(innerVF,LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, 0,0,0,0, 0,0,0,0), NULL, 0, LAYOUT_FILL_X|LAYOUT_FILL_Y);
+	input_text->setEditable(false);
+	new FXButton(innerVF, "Clear", NULL, this, ID_CLEAR, BUTTON_NORMAL|LAYOUT_RIGHT);
+	
+
+}
+
+MainWindow::~MainWindow()
+{
+	if (connected_device)
+		hid_close(connected_device);
+	hid_exit();
+	delete title_font;
+}
+
+void
+MainWindow::create()
+{
+	FXMainWindow::create();
+	show();
+
+	onRescan(NULL, 0, NULL);
+	
+
+#ifdef __APPLE__
+	init_apple_message_system();
+#endif
+	
+	getApp()->addTimeout(this, ID_MAC_TIMER,
+		50 * timeout_scalar /*50ms*/);
+}
+
+long
+MainWindow::onConnect(FXObject *sender, FXSelector sel, void *ptr)
+{
+	if (connected_device != NULL)
+		return 1;
+	
+	FXint cur_item = device_list->getCurrentItem();
+	if (cur_item < 0)
+		return -1;
+	FXListItem *item = device_list->getItem(cur_item);
+	if (!item)
+		return -1;
+	struct hid_device_info *device_info = (struct hid_device_info*) item->getData();
+	if (!device_info)
+		return -1;
+	
+	connected_device =  hid_open_path(device_info->path);
+	
+	if (!connected_device) {
+		FXMessageBox::error(this, MBOX_OK, "Device Error", "Unable To Connect to Device");
+		return -1;
+	}
+	
+	hid_set_nonblocking(connected_device, 1);
+
+	getApp()->addTimeout(this, ID_TIMER,
+		5 * timeout_scalar /*5ms*/);
+	
+	FXString s;
+	s.format("Connected to: %04hx:%04hx -", device_info->vendor_id, device_info->product_id);
+	s += FXString(" ") + device_info->manufacturer_string;
+	s += FXString(" ") + device_info->product_string;
+	connected_label->setText(s);
+	output_button->enable();
+	feature_button->enable();
+	get_feature_button->enable();
+	connect_button->disable();
+	disconnect_button->enable();
+	input_text->setText("");
+
+
+	return 1;
+}
+
+long
+MainWindow::onDisconnect(FXObject *sender, FXSelector sel, void *ptr)
+{
+	hid_close(connected_device);
+	connected_device = NULL;
+	connected_label->setText("Disconnected");
+	output_button->disable();
+	feature_button->disable();
+	get_feature_button->disable();
+	connect_button->enable();
+	disconnect_button->disable();
+
+	getApp()->removeTimeout(this, ID_TIMER);
+	
+	return 1;
+}
+
+long
+MainWindow::onRescan(FXObject *sender, FXSelector sel, void *ptr)
+{
+	struct hid_device_info *cur_dev;
+
+	device_list->clearItems();
+	
+	// List the Devices
+	hid_free_enumeration(devices);
+	devices = hid_enumerate(0x0, 0x0);
+	cur_dev = devices;	
+	while (cur_dev) {
+		// Add it to the List Box.
+		FXString s;
+		FXString usage_str;
+		s.format("%04hx:%04hx -", cur_dev->vendor_id, cur_dev->product_id);
+		s += FXString(" ") + cur_dev->manufacturer_string;
+		s += FXString(" ") + cur_dev->product_string;
+		usage_str.format(" (usage: %04hx:%04hx) ", cur_dev->usage_page, cur_dev->usage);
+		s += usage_str;
+		FXListItem *li = new FXListItem(s, NULL, cur_dev);
+		device_list->appendItem(li);
+		
+		cur_dev = cur_dev->next;
+	}
+
+	if (device_list->getNumItems() == 0)
+		device_list->appendItem("*** No Devices Connected ***");
+	else {
+		device_list->selectItem(0);
+	}
+
+	return 1;
+}
+
+size_t
+MainWindow::getDataFromTextField(FXTextField *tf, char *buf, size_t len)
+{
+	const char *delim = " ,{}\t\r\n";
+	FXString data = tf->getText();
+	const FXchar *d = data.text();
+	size_t i = 0;
+	
+	// Copy the string from the GUI.
+	size_t sz = strlen(d);
+	char *str = (char*) malloc(sz+1);
+	strcpy(str, d);
+	
+	// For each token in the string, parse and store in buf[].
+	char *token = strtok(str, delim);
+	while (token) {
+		char *endptr;
+		long int val = strtol(token, &endptr, 0);
+		buf[i++] = val;
+		token = strtok(NULL, delim);
+	}
+	
+	free(str);
+	return i;
+}
+
+/* getLengthFromTextField()
+   Returns length:
+	 0: empty text field
+	>0: valid length
+	-1: invalid length */
+int
+MainWindow::getLengthFromTextField(FXTextField *tf)
+{
+	long int len;
+	FXString str = tf->getText();
+	size_t sz = str.length();
+
+	if (sz > 0) {
+		char *endptr;
+		len = strtol(str.text(), &endptr, 0);
+		if (endptr != str.text() && *endptr == '\0') {
+			if (len <= 0) {
+				FXMessageBox::error(this, MBOX_OK, "Invalid length", "Enter a length greater than zero.");
+				return -1;
+			}
+			return len;
+		}
+		else
+			return -1;
+	}
+
+	return 0;
+}
+
+long
+MainWindow::onSendOutputReport(FXObject *sender, FXSelector sel, void *ptr)
+{
+	char buf[256];
+	size_t data_len, len;
+	int textfield_len;
+
+	memset(buf, 0x0, sizeof(buf));
+	textfield_len = getLengthFromTextField(output_len);
+	data_len = getDataFromTextField(output_text, buf, sizeof(buf));
+
+	if (textfield_len < 0) {
+		FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal.");
+		return 1;
+	}
+
+	if (textfield_len > sizeof(buf)) {
+		FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long.");
+		return 1;
+	}
+
+	len = (textfield_len)? textfield_len: data_len;
+
+	int res = hid_write(connected_device, (const unsigned char*)buf, len);
+	if (res < 0) {
+		FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not write to device. Error reported was: %ls", hid_error(connected_device));
+	}
+	
+	return 1;
+}
+
+long
+MainWindow::onSendFeatureReport(FXObject *sender, FXSelector sel, void *ptr)
+{
+	char buf[256];
+	size_t data_len, len;
+	int textfield_len;
+
+	memset(buf, 0x0, sizeof(buf));
+	textfield_len = getLengthFromTextField(feature_len);
+	data_len = getDataFromTextField(feature_text, buf, sizeof(buf));
+
+	if (textfield_len < 0) {
+		FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is invalid. Please enter a number in hex, octal, or decimal.");
+		return 1;
+	}
+
+	if (textfield_len > sizeof(buf)) {
+		FXMessageBox::error(this, MBOX_OK, "Invalid length", "Length field is too long.");
+		return 1;
+	}
+
+	len = (textfield_len)? textfield_len: data_len;
+
+	int res = hid_send_feature_report(connected_device, (const unsigned char*)buf, len); 
+	if (res < 0) {
+		FXMessageBox::error(this, MBOX_OK, "Error Writing", "Could not send feature report to device. Error reported was: %ls", hid_error(connected_device));
+	}
+
+	return 1;
+}
+
+long
+MainWindow::onGetFeatureReport(FXObject *sender, FXSelector sel, void *ptr)
+{
+	char buf[256];
+	size_t len;
+
+	memset(buf, 0x0, sizeof(buf));
+	len = getDataFromTextField(get_feature_text, buf, sizeof(buf));
+
+	if (len != 1) {
+		FXMessageBox::error(this, MBOX_OK, "Too many numbers", "Enter only a single report number in the text field");
+	}
+
+	int res = hid_get_feature_report(connected_device, (unsigned char*)buf, sizeof(buf));
+	if (res < 0) {
+		FXMessageBox::error(this, MBOX_OK, "Error Getting Report", "Could not get feature report from device. Error reported was: %ls", hid_error(connected_device));
+	}
+
+	if (res > 0) {
+		FXString s;
+		s.format("Returned Feature Report. %d bytes:\n", res);
+		for (int i = 0; i < res; i++) {
+			FXString t;
+			t.format("%02hhx ", buf[i]);
+			s += t;
+			if ((i+1) % 4 == 0)
+				s += " ";
+			if ((i+1) % 16 == 0)
+				s += "\n";
+		}
+		s += "\n";
+		input_text->appendText(s);
+		input_text->setBottomLine(INT_MAX);
+	}
+	
+	return 1;
+}
+
+long
+MainWindow::onClear(FXObject *sender, FXSelector sel, void *ptr)
+{
+	input_text->setText("");
+	return 1;
+}
+
+long
+MainWindow::onTimeout(FXObject *sender, FXSelector sel, void *ptr)
+{
+	unsigned char buf[256];
+	int res = hid_read(connected_device, buf, sizeof(buf));
+	
+	if (res > 0) {
+		FXString s;
+		s.format("Received %d bytes:\n", res);
+		for (int i = 0; i < res; i++) {
+			FXString t;
+			t.format("%02hhx ", buf[i]);
+			s += t;
+			if ((i+1) % 4 == 0)
+				s += " ";
+			if ((i+1) % 16 == 0)
+				s += "\n";
+		}
+		s += "\n";
+		input_text->appendText(s);
+		input_text->setBottomLine(INT_MAX);
+	}
+	if (res < 0) {
+		input_text->appendText("hid_read() returned error\n");
+		input_text->setBottomLine(INT_MAX);
+	}
+
+	getApp()->addTimeout(this, ID_TIMER,
+		5 * timeout_scalar /*5ms*/);
+	return 1;
+}
+
+long
+MainWindow::onMacTimeout(FXObject *sender, FXSelector sel, void *ptr)
+{
+#ifdef __APPLE__
+	check_apple_events();
+	
+	getApp()->addTimeout(this, ID_MAC_TIMER,
+		50 * timeout_scalar /*50ms*/);
+#endif
+
+	return 1;
+}
+
+int main(int argc, char **argv)
+{
+	FXApp app("HIDAPI Test Application", "Signal 11 Software");
+	app.init(argc, argv);
+	g_main_window = new MainWindow(&app);
+	app.create();
+	app.run();
+	return 0;
+}
diff --git a/source/src/hidapi/testgui/testgui.sln b/source/src/hidapi/testgui/testgui.sln
new file mode 100644
index 0000000..35abcec
--- /dev/null
+++ b/source/src/hidapi/testgui/testgui.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testgui", "testgui.vcproj", "{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.ActiveCfg = Debug|Win32
+		{08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Debug|Win32.Build.0 = Debug|Win32
+		{08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.ActiveCfg = Release|Win32
+		{08769AC3-785A-4DDC-BFC7-1775414B7AB7}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/source/src/hidapi/testgui/testgui.vcproj b/source/src/hidapi/testgui/testgui.vcproj
new file mode 100644
index 0000000..91be8ee
--- /dev/null
+++ b/source/src/hidapi/testgui/testgui.vcproj
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="testgui"
+	ProjectGUID="{08769AC3-785A-4DDC-BFC7-1775414B7AB7}"
+	RootNamespace="testgui"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;..\..\hidapi-externals\fox\include&quot;;..\hidapi"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="setupapi.lib fox-1.6.lib"
+				OutputFile="$(ProjectName).exe"
+				LinkIncremental="2"
+				AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;&quot;..\..\hidapi-externals\fox\lib&quot;"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				EntryPointSymbol="mainCRTStartup"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="&quot;..\..\hidapi-externals\fox\include&quot;;..\hidapi"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="setupapi.lib fox-1.6.lib"
+				OutputFile="$(ProjectName).exe"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="..\hidapi\objfre_wxp_x86\i386;&quot;..\..\hidapi-externals\fox\lib&quot;"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				EntryPointSymbol="mainCRTStartup"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine=""
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\windows\hid.c"
+				>
+			</File>
+			<File
+				RelativePath=".\test.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\hidapi\hidapi.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+		<File
+			RelativePath=".\ReadMe.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/source/src/hidapi/udev/99-hid.rules b/source/src/hidapi/udev/99-hid.rules
new file mode 100644
index 0000000..0385f50
--- /dev/null
+++ b/source/src/hidapi/udev/99-hid.rules
@@ -0,0 +1,33 @@
+# This is a sample udev file for HIDAPI devices which changes the permissions
+# to 0666 (world readable/writable) for a specified device on Linux systems.
+
+
+# If you are using the libusb implementation of hidapi (libusb/hid.c), then
+# use something like the following line, substituting the VID and PID with
+# those of your device. Note that for kernels before 2.6.24, you will need
+# to substitute "usb" with "usb_device". It shouldn't hurt to use two lines
+# (one each way) for compatibility with older systems.
+
+# HIDAPI/libusb
+SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
+
+
+# If you are using the hidraw implementation (linux/hid.c), then do something
+# like the following, substituting the VID and PID with your device. Busnum 1
+# is USB.
+
+# HIDAPI/hidraw
+KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
+
+# Once done, optionally rename this file for your device, and drop it into
+# /etc/udev/rules.d and unplug and re-plug your device. This is all that is
+# necessary to see the new permissions. Udev does not have to be restarted.
+
+# Note that the hexadecimal values for VID and PID are case sensitive and
+# must be lower case.
+
+# If you think permissions of 0666 are too loose, then see:
+# http://reactivated.net/writing_udev_rules.html for more information on finer
+# grained permission setting. For example, it might be sufficient to just
+# set the group or user owner for specific devices (for example the plugdev
+# group on some systems).
diff --git a/source/src/hidapi/windows/Makefile-manual b/source/src/hidapi/windows/Makefile-manual
new file mode 100644
index 0000000..ac471d6
--- /dev/null
+++ b/source/src/hidapi/windows/Makefile-manual
@@ -0,0 +1,14 @@
+
+
+OS=$(shell uname)
+
+ifneq (,$(findstring MINGW,$(OS)))
+	FILE=Makefile.mingw
+endif
+
+ifeq ($(FILE), )
+all:
+	$(error Your platform ${OS} is not supported at this time.)
+endif
+
+include $(FILE)
diff --git a/source/src/hidapi/windows/Makefile.am b/source/src/hidapi/windows/Makefile.am
new file mode 100644
index 0000000..97e261a
--- /dev/null
+++ b/source/src/hidapi/windows/Makefile.am
@@ -0,0 +1,16 @@
+lib_LTLIBRARIES = libhidapi.la
+libhidapi_la_SOURCES = hid.c
+libhidapi_la_LDFLAGS = $(LTLDFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
+libhidapi_la_LIBADD = $(LIBS)
+
+hdrdir = $(includedir)/hidapi
+hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
+
+EXTRA_DIST = \
+  ddk_build \
+  hidapi.vcproj \
+  hidtest.vcproj \
+  Makefile-manual \
+  Makefile.mingw \
+  hidapi.sln
diff --git a/source/src/hidapi/windows/Makefile.mingw b/source/src/hidapi/windows/Makefile.mingw
new file mode 100644
index 0000000..b800004
--- /dev/null
+++ b/source/src/hidapi/windows/Makefile.mingw
@@ -0,0 +1,35 @@
+###########################################
+# Simple Makefile for HIDAPI test program
+#
+# Alan Ott
+# Signal 11 Software
+# 2010-06-01
+###########################################
+
+all: hidtest libhidapi.dll
+
+CC=gcc
+CXX=g++
+COBJS=hid.o
+CPPOBJS=../hidtest/hidtest.o
+OBJS=$(COBJS) $(CPPOBJS)
+CFLAGS=-I../hidapi -g -c
+LIBS= -lsetupapi
+DLL_LDFLAGS = -mwindows -lsetupapi
+
+hidtest: $(OBJS)
+	g++ -g $^ $(LIBS) -o hidtest
+
+libhidapi.dll: $(OBJS)
+	$(CC) -g $^ $(DLL_LDFLAGS) -o libhidapi.dll
+
+$(COBJS): %.o: %.c
+	$(CC) $(CFLAGS) $< -o $@
+
+$(CPPOBJS): %.o: %.cpp
+	$(CXX) $(CFLAGS) $< -o $@
+
+clean:
+	rm *.o ../hidtest/*.o hidtest.exe
+
+.PHONY: clean
diff --git a/source/src/hidapi/windows/ddk_build/hidapi.def b/source/src/hidapi/windows/ddk_build/hidapi.def
new file mode 100644
index 0000000..05e35af
--- /dev/null
+++ b/source/src/hidapi/windows/ddk_build/hidapi.def
@@ -0,0 +1,17 @@
+LIBRARY   hidapi
+EXPORTS
+   hid_open    @1
+   hid_write   @2
+   hid_read    @3
+   hid_close   @4
+   hid_get_product_string @5
+   hid_get_manufacturer_string @6
+   hid_get_serial_number_string @7
+   hid_get_indexed_string @8
+   hid_error @9
+   hid_set_nonblocking @10
+   hid_enumerate @11
+   hid_open_path @12
+   hid_send_feature_report @13
+   hid_get_feature_report @14
+   
\ No newline at end of file
diff --git a/source/src/hidapi/windows/ddk_build/makefile b/source/src/hidapi/windows/ddk_build/makefile
new file mode 100644
index 0000000..637f712
--- /dev/null
+++ b/source/src/hidapi/windows/ddk_build/makefile
@@ -0,0 +1,49 @@
+#############################################################################
+#
+#               Copyright (C) Microsoft Corporation 1995, 1996
+#       All Rights Reserved.
+#
+#       MAKEFILE for HID directory
+#
+#############################################################################
+
+!IFDEF WIN95_BUILD
+
+ROOT=..\..\..\..
+
+VERSIONLIST = debug retail
+IS_32 = TRUE
+IS_SDK = TRUE
+IS_PRIVATE = TRUE
+IS_SDK = TRUE
+IS_DDK = TRUE
+WIN32 = TRUE
+COMMONMKFILE = hidapi.mk
+
+!include $(ROOT)\dev\master.mk
+
+
+!ELSE
+
+#
+# DO NOT EDIT THIS FILE!!!  Edit .\sources. if you want to add a new source
+# file to this component.  This file merely indirects to the real make file
+# that is shared by all the driver components of the Windows NT DDK
+#
+
+!IF DEFINED(_NT_TARGET_VERSION)
+!	IF $(_NT_TARGET_VERSION)>=0x501
+!		INCLUDE $(NTMAKEENV)\makefile.def
+!	ELSE
+#               Only warn once per directory
+!               INCLUDE $(NTMAKEENV)\makefile.plt
+!               IF "$(BUILD_PASS)"=="PASS1"
+!		    message BUILDMSG: Warning : The sample "$(MAKEDIR)" is not valid for the current OS target.
+!               ENDIF
+!	ENDIF
+!ELSE
+!	INCLUDE $(NTMAKEENV)\makefile.def
+!ENDIF
+
+!ENDIF
+
diff --git a/source/src/hidapi/windows/ddk_build/sources b/source/src/hidapi/windows/ddk_build/sources
new file mode 100644
index 0000000..7f06a09
--- /dev/null
+++ b/source/src/hidapi/windows/ddk_build/sources
@@ -0,0 +1,23 @@
+TARGETNAME=hidapi
+TARGETTYPE=DYNLINK
+UMTYPE=console
+UMENTRY=main
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+TARGETLIBS=$(SDK_LIB_PATH)\hid.lib \
+           $(SDK_LIB_PATH)\setupapi.lib \
+           $(SDK_LIB_PATH)\kernel32.lib \
+           $(SDK_LIB_PATH)\comdlg32.lib
+
+USE_MSVCRT=1
+
+INCLUDES= ..\..\hidapi
+SOURCES= ..\hid.c \
+
+
+TARGET_DESTINATION=retail
+
+MUI=0
+MUI_COMMENT="HID Interface DLL"
+
diff --git a/source/src/hidapi/windows/hid.c b/source/src/hidapi/windows/hid.c
new file mode 100644
index 0000000..3795e18
--- /dev/null
+++ b/source/src/hidapi/windows/hid.c
@@ -0,0 +1,988 @@
+/*******************************************************
+ HIDAPI - Multi-Platform library for
+ communication with HID devices.
+
+ Alan Ott
+ Signal 11 Software
+
+ 8/22/2009
+
+ Copyright 2009, All Rights Reserved.
+ 
+ At the discretion of the user of this library,
+ this software may be licensed under the terms of the
+ GNU General Public License v3, a BSD-Style license, or the
+ original HIDAPI license as outlined in the LICENSE.txt,
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
+ files located at the root of the source distribution.
+ These files may also be found in the public source
+ code repository located at:
+        http://github.com/signal11/hidapi .
+********************************************************/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include <windows.h>
+
+#if 0 /* can cause redefinition errors on some toolchains */
+#ifdef __MINGW32__
+#include <ntdef.h>
+#include <winbase.h>
+#endif
+
+#ifdef __CYGWIN__
+#include <ntdef.h>
+#define _wcsdup wcsdup
+#endif
+#endif /* */
+
+#ifndef _NTDEF_
+typedef LONG NTSTATUS;
+#endif
+
+/* SDL C runtime functions */
+#include "SDL_stdinc.h"
+
+#define calloc SDL_calloc
+#define free SDL_free
+#define malloc SDL_malloc
+#define memcpy SDL_memcpy
+#define memset SDL_memset
+#define strcmp SDL_strcmp
+#define strlen SDL_strlen
+#define strncpy SDL_strlcpy
+#define strstr SDL_strstr
+#define strtol SDL_strtol
+#define wcscmp SDL_wcscmp
+#define _wcsdup SDL_wcsdup
+
+/* The maximum number of characters that can be passed into the
+   HidD_Get*String() functions without it failing.*/
+#define MAX_STRING_WCHARS 0xFFF
+
+/*#define HIDAPI_USE_DDK*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+	#include <setupapi.h>
+	#include <winioctl.h>
+	#ifdef HIDAPI_USE_DDK
+		#include <hidsdi.h>
+	#endif
+
+	/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
+	#define HID_OUT_CTL_CODE(id)  \
+		CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+	#define IOCTL_HID_GET_FEATURE                   HID_OUT_CTL_CODE(100)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#include "../hidapi/hidapi.h"
+
+#undef MIN
+#define MIN(x,y) ((x) < (y)? (x): (y))
+
+#ifdef _MSC_VER
+	/* Thanks Microsoft, but I know how to use strncpy(). */
+	#pragma warning(disable:4996)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef HIDAPI_USE_DDK
+	/* Since we're not building with the DDK, and the HID header
+	   files aren't part of the SDK, we have to define all this
+	   stuff here. In lookup_functions(), the function pointers
+	   defined below are set. */
+	typedef struct _HIDD_ATTRIBUTES{
+		ULONG Size;
+		USHORT VendorID;
+		USHORT ProductID;
+		USHORT VersionNumber;
+	} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
+
+	typedef USHORT USAGE;
+	typedef struct _HIDP_CAPS {
+		USAGE Usage;
+		USAGE UsagePage;
+		USHORT InputReportByteLength;
+		USHORT OutputReportByteLength;
+		USHORT FeatureReportByteLength;
+		USHORT Reserved[17];
+		USHORT fields_not_used_by_hidapi[10];
+	} HIDP_CAPS, *PHIDP_CAPS;
+	typedef void* PHIDP_PREPARSED_DATA;
+	#define HIDP_STATUS_SUCCESS 0x110000
+
+	typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
+	typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
+	typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
+	typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
+	typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
+	typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
+	typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
+	typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
+	typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
+	typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
+	typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
+	typedef BOOLEAN(__stdcall *HidD_SetOutputReport_ )(HANDLE handle, PVOID buffer, ULONG buffer_len);
+	static HidD_GetAttributes_ HidD_GetAttributes;
+	static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
+	static HidD_GetManufacturerString_ HidD_GetManufacturerString;
+	static HidD_GetProductString_ HidD_GetProductString;
+	static HidD_SetFeature_ HidD_SetFeature;
+	static HidD_GetFeature_ HidD_GetFeature;
+	static HidD_GetIndexedString_ HidD_GetIndexedString;
+	static HidD_GetPreparsedData_ HidD_GetPreparsedData;
+	static HidD_FreePreparsedData_ HidD_FreePreparsedData;
+	static HidP_GetCaps_ HidP_GetCaps;
+	static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
+	static HidD_SetOutputReport_ HidD_SetOutputReport;
+
+	static HMODULE lib_handle = NULL;
+	static BOOLEAN initialized = FALSE;
+#endif /* HIDAPI_USE_DDK */
+
+struct hid_device_ {
+		HANDLE device_handle;
+		BOOL blocking;
+		USHORT output_report_length;
+		size_t input_report_length;
+		void *last_error_str;
+		DWORD last_error_num;
+		BOOL read_pending;
+		char *read_buf;
+		OVERLAPPED ol;
+};
+
+static hid_device *new_hid_device()
+{
+	hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
+	dev->device_handle = INVALID_HANDLE_VALUE;
+	dev->blocking = TRUE;
+	dev->output_report_length = 0;
+	dev->input_report_length = 0;
+	dev->last_error_str = NULL;
+	dev->last_error_num = 0;
+	dev->read_pending = FALSE;
+	dev->read_buf = NULL;
+	memset(&dev->ol, 0, sizeof(dev->ol));
+	dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
+
+	return dev;
+}
+
+static void free_hid_device(hid_device *dev)
+{
+	CloseHandle(dev->ol.hEvent);
+	CloseHandle(dev->device_handle);
+	LocalFree(dev->last_error_str);
+	free(dev->read_buf);
+	free(dev);
+}
+
+static void register_error(hid_device *device, const char *op)
+{
+	WCHAR *ptr, *msg;
+
+	DWORD count = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+		FORMAT_MESSAGE_FROM_SYSTEM |
+		FORMAT_MESSAGE_IGNORE_INSERTS,
+		NULL,
+		GetLastError(),
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPWSTR)&msg, 0/*sz*/,
+		NULL);
+	if (!count)
+		return;
+	
+	/* Get rid of the CR and LF that FormatMessage() sticks at the
+	   end of the message. Thanks Microsoft! */
+	ptr = msg;
+	while (*ptr) {
+		if (*ptr == '\r') {
+			*ptr = 0x0000;
+			break;
+		}
+		ptr++;
+	}
+
+	/* Store the message off in the Device entry so that
+	   the hid_error() function can pick it up. */
+	LocalFree(device->last_error_str);
+	device->last_error_str = msg;
+}
+
+#ifndef HIDAPI_USE_DDK
+static int lookup_functions()
+{
+	lib_handle = LoadLibraryA("hid.dll");
+	if (lib_handle) {
+#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
+		RESOLVE(HidD_GetAttributes);
+		RESOLVE(HidD_GetSerialNumberString);
+		RESOLVE(HidD_GetManufacturerString);
+		RESOLVE(HidD_GetProductString);
+		RESOLVE(HidD_SetFeature);
+		RESOLVE(HidD_GetFeature);
+		RESOLVE(HidD_GetIndexedString);
+		RESOLVE(HidD_GetPreparsedData);
+		RESOLVE(HidD_FreePreparsedData);
+		RESOLVE(HidP_GetCaps);
+		RESOLVE(HidD_SetNumInputBuffers);
+		RESOLVE(HidD_SetOutputReport);
+#undef RESOLVE
+	}
+	else
+		return -1;
+
+	return 0;
+}
+#endif
+
+static HANDLE open_device(const char *path, BOOL enumerate, BOOL bExclusive )
+{
+	HANDLE handle;
+	// Opening with access 0 causes keyboards to stop responding in some system configurations
+	// http://steamcommunity.com/discussions/forum/1/1843493219428923893
+	// Thanks to co-wie (Ka-wei Low <kawei@mac.com>) for help narrowing down the problem on his system
+	//DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
+	DWORD desired_access = ( GENERIC_WRITE | GENERIC_READ );
+	DWORD share_mode = bExclusive ? 0 : ( FILE_SHARE_READ | FILE_SHARE_WRITE );
+
+	handle = CreateFileA(path,
+		desired_access,
+		share_mode,
+		NULL,
+		OPEN_EXISTING,
+		FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
+		0);
+
+	return handle;
+}
+
+int HID_API_EXPORT hid_init(void)
+{
+#ifndef HIDAPI_USE_DDK
+	if (!initialized) {
+		if (lookup_functions() < 0) {
+			hid_exit();
+			return -1;
+		}
+		initialized = TRUE;
+	}
+#endif
+	return 0;
+}
+
+int HID_API_EXPORT hid_exit(void)
+{
+#ifndef HIDAPI_USE_DDK
+	if (lib_handle)
+		FreeLibrary(lib_handle);
+	lib_handle = NULL;
+	initialized = FALSE;
+#endif
+	return 0;
+}
+
+struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+	BOOL res;
+	struct hid_device_info *root = NULL; /* return object */
+	struct hid_device_info *cur_dev = NULL;
+
+	/* Windows objects for interacting with the driver. */
+	GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
+	SP_DEVINFO_DATA devinfo_data;
+	SP_DEVICE_INTERFACE_DATA device_interface_data;
+	SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
+	HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
+	int device_index = 0;
+	int i;
+
+	if (hid_init() < 0)
+		return NULL;
+
+	/* Initialize the Windows objects. */
+	memset(&devinfo_data, 0x0, sizeof(devinfo_data));
+	devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
+	device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+	/* Get information for all the devices belonging to the HID class. */
+	device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+	
+	/* Iterate over each device in the HID class, looking for the right one. */
+	
+	for (;;) {
+		HANDLE write_handle = INVALID_HANDLE_VALUE;
+		DWORD required_size = 0;
+		HIDD_ATTRIBUTES attrib;
+
+		res = SetupDiEnumDeviceInterfaces(device_info_set,
+			NULL,
+			&InterfaceClassGuid,
+			device_index,
+			&device_interface_data);
+		
+		if (!res) {
+			/* A return of FALSE from this function means that
+			   there are no more devices. */
+			break;
+		}
+
+		/* Call with 0-sized detail size, and let the function
+		   tell us how long the detail struct needs to be. The
+		   size is put in &required_size. */
+		res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
+			&device_interface_data,
+			NULL,
+			0,
+			&required_size,
+			NULL);
+
+		/* Allocate a long enough structure for device_interface_detail_data. */
+		device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
+		device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
+
+		/* Get the detailed data for this device. The detail data gives us
+		   the device path for this device, which is then passed into
+		   CreateFile() to get a handle to the device. */
+		res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
+			&device_interface_data,
+			device_interface_detail_data,
+			required_size,
+			NULL,
+			NULL);
+
+		if (!res) {
+			/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
+			   Continue to the next device. */
+			goto cont;
+		}
+
+		/* Make sure this device is of Setup Class "HIDClass" and has a
+		   driver bound to it. */
+		for (i = 0; ; i++) {
+			char driver_name[256];
+
+			/* Populate devinfo_data. This function will return failure
+			   when there are no more interfaces left. */
+			res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
+			if (!res)
+				goto cont;
+
+			res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
+			               SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
+			if (!res)
+				goto cont;
+
+			if (strcmp(driver_name, "HIDClass") == 0) {
+				/* See if there's a driver bound. */
+				res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
+				           SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
+				if (res)
+					break;
+			}
+		}
+
+		//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
+
+		/* Open a handle to the device */
+		write_handle = open_device(device_interface_detail_data->DevicePath, TRUE, FALSE);
+
+		/* Check validity of write_handle. */
+		if (write_handle == INVALID_HANDLE_VALUE) {
+			/* Unable to open the device. */
+			//register_error(dev, "CreateFile");
+			goto cont_close;
+		}		
+
+
+		/* Get the Vendor ID and Product ID for this device. */
+		attrib.Size = sizeof(HIDD_ATTRIBUTES);
+		HidD_GetAttributes(write_handle, &attrib);
+		//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
+
+		/* Check the VID/PID to see if we should add this
+		   device to the enumeration list. */
+		if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
+		    (product_id == 0x0 || attrib.ProductID == product_id)) {
+
+			#define WSTR_LEN 512
+			const char *str;
+			struct hid_device_info *tmp;
+			PHIDP_PREPARSED_DATA pp_data = NULL;
+			HIDP_CAPS caps;
+			BOOLEAN hidp_res;
+			NTSTATUS nt_res;
+			wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
+			size_t len;
+
+			/* VID/PID match. Create the record. */
+			tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
+			if (cur_dev) {
+				cur_dev->next = tmp;
+			}
+			else {
+				root = tmp;
+			}
+			cur_dev = tmp;
+
+			/* Get the Usage Page and Usage for this device. */
+			hidp_res = HidD_GetPreparsedData(write_handle, &pp_data);
+			if (hidp_res) {
+				nt_res = HidP_GetCaps(pp_data, &caps);
+				if (nt_res == HIDP_STATUS_SUCCESS) {
+					cur_dev->usage_page = caps.UsagePage;
+					cur_dev->usage = caps.Usage;
+				}
+
+				HidD_FreePreparsedData(pp_data);
+			}
+			
+			/* Fill out the record */
+			cur_dev->next = NULL;
+			str = device_interface_detail_data->DevicePath;
+			if (str) {
+				len = strlen(str);
+				cur_dev->path = (char*) calloc(len+1, sizeof(char));
+				strncpy(cur_dev->path, str, len+1);
+				cur_dev->path[len] = '\0';
+			}
+			else
+				cur_dev->path = NULL;
+
+			/* Serial Number */
+			hidp_res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
+			wstr[WSTR_LEN-1] = 0x0000;
+			if (hidp_res) {
+				cur_dev->serial_number = _wcsdup(wstr);
+			}
+
+			/* Manufacturer String */
+			hidp_res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
+			wstr[WSTR_LEN-1] = 0x0000;
+			if (hidp_res) {
+				cur_dev->manufacturer_string = _wcsdup(wstr);
+			}
+
+			/* Product String */
+			hidp_res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
+			wstr[WSTR_LEN-1] = 0x0000;
+			if (hidp_res) {
+				cur_dev->product_string = _wcsdup(wstr);
+			}
+
+			/* VID/PID */
+			cur_dev->vendor_id = attrib.VendorID;
+			cur_dev->product_id = attrib.ProductID;
+
+			/* Release Number */
+			cur_dev->release_number = attrib.VersionNumber;
+
+			/* Interface Number. It can sometimes be parsed out of the path
+			   on Windows if a device has multiple interfaces. See
+			   http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
+			   search for "Hardware IDs for HID Devices" at MSDN. If it's not
+			   in the path, it's set to -1. */
+			cur_dev->interface_number = -1;
+			if (cur_dev->path) {
+				char *interface_component = strstr(cur_dev->path, "&mi_");
+				if (interface_component) {
+					char *hex_str = interface_component + 4;
+					char *endptr = NULL;
+					cur_dev->interface_number = strtol(hex_str, &endptr, 16);
+					if (endptr == hex_str) {
+						/* The parsing failed. Set interface_number to -1. */
+						cur_dev->interface_number = -1;
+					}
+				}
+			}
+		}
+
+cont_close:
+		CloseHandle(write_handle);
+cont:
+		/* We no longer need the detail data. It can be freed */
+		free(device_interface_detail_data);
+
+		device_index++;
+
+	}
+
+	/* Close the device information handle. */
+	SetupDiDestroyDeviceInfoList(device_info_set);
+
+	return root;
+
+}
+
+void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
+{
+	/* TODO: Merge this with the Linux version. This function is platform-independent. */
+	struct hid_device_info *d = devs;
+	while (d) {
+		struct hid_device_info *next = d->next;
+		free(d->path);
+		free(d->serial_number);
+		free(d->manufacturer_string);
+		free(d->product_string);
+		free(d);
+		d = next;
+	}
+}
+
+
+HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
+{
+	/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
+	struct hid_device_info *devs, *cur_dev;
+	const char *path_to_open = NULL;
+	hid_device *handle = NULL;
+	
+	devs = hid_enumerate(vendor_id, product_id);
+	cur_dev = devs;
+	while (cur_dev) {
+		if (cur_dev->vendor_id == vendor_id &&
+		    cur_dev->product_id == product_id) {
+			if (serial_number) {
+				if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
+					path_to_open = cur_dev->path;
+					break;
+				}
+			}
+			else {
+				path_to_open = cur_dev->path;
+				break;
+			}
+		}
+		cur_dev = cur_dev->next;
+	}
+
+	if (path_to_open) {
+		/* Open the device */
+		handle = hid_open_path(path_to_open, 0);
+	}
+
+	hid_free_enumeration(devs);
+	
+	return handle;
+}
+
+HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
+{
+	hid_device *dev;
+	HIDP_CAPS caps;
+	PHIDP_PREPARSED_DATA pp_data = NULL;
+	BOOLEAN res;
+	NTSTATUS nt_res;
+
+	if (hid_init() < 0) {
+		return NULL;
+	}
+
+	dev = new_hid_device();
+
+	/* Open a handle to the device */
+	dev->device_handle = open_device(path, FALSE, bExclusive);
+
+	/* Check validity of write_handle. */
+	if (dev->device_handle == INVALID_HANDLE_VALUE) {
+		/* Unable to open the device. */
+		register_error(dev, "CreateFile");
+		goto err;
+	}
+
+	/* Set the Input Report buffer size to 64 reports. */
+	res = HidD_SetNumInputBuffers(dev->device_handle, 64);
+	if (!res) {
+		register_error(dev, "HidD_SetNumInputBuffers");
+		goto err;
+	}
+
+	/* Get the Input Report length for the device. */
+	res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
+	if (!res) {
+		register_error(dev, "HidD_GetPreparsedData");
+		goto err;
+	}
+	nt_res = HidP_GetCaps(pp_data, &caps);
+	if (nt_res != HIDP_STATUS_SUCCESS) {
+		register_error(dev, "HidP_GetCaps");	
+		goto err_pp_data;
+	}
+	dev->output_report_length = caps.OutputReportByteLength;
+	dev->input_report_length = caps.InputReportByteLength;
+	HidD_FreePreparsedData(pp_data);
+
+	dev->read_buf = (char*) malloc(dev->input_report_length);
+
+	return dev;
+
+err_pp_data:
+		HidD_FreePreparsedData(pp_data);
+err:	
+		free_hid_device(dev);
+		return NULL;
+}
+
+int HID_API_EXPORT HID_API_CALL hid_write_output_report(hid_device *dev, const unsigned char *data, size_t length)
+{
+	BOOL res;
+	res = HidD_SetOutputReport(dev->device_handle, (void *)data, (ULONG)length);
+	if (res)
+		return (int)length;
+	else
+		return -1;
+}
+
+int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
+{
+	DWORD bytes_written;
+	BOOL res;
+	size_t stashed_length = length;
+	OVERLAPPED ol;
+	unsigned char *buf;
+	memset(&ol, 0, sizeof(ol));
+
+	/* Make sure the right number of bytes are passed to WriteFile. Windows
+	   expects the number of bytes which are in the _longest_ report (plus
+	   one for the report number) bytes even if the data is a report
+	   which is shorter than that. Windows gives us this value in
+	   caps.OutputReportByteLength. If a user passes in fewer bytes than this,
+	   create a temporary buffer which is the proper size. */
+	if (length >= dev->output_report_length) {
+		/* The user passed the right number of bytes. Use the buffer as-is. */
+		buf = (unsigned char *) data;
+	} else {
+		/* Create a temporary buffer and copy the user's data
+		   into it, padding the rest with zeros. */
+		buf = (unsigned char *) malloc(dev->output_report_length);
+		memcpy(buf, data, length);
+		memset(buf + length, 0, dev->output_report_length - length);
+		length = dev->output_report_length;
+	}
+	if (length > 512)
+	{
+		return hid_write_output_report( dev, data, stashed_length );
+	}
+	else
+	{
+		res = WriteFile( dev->device_handle, buf, ( DWORD ) length, NULL, &ol );
+		if (!res) {
+			if (GetLastError() != ERROR_IO_PENDING) {
+				/* WriteFile() failed. Return error. */
+				register_error(dev, "WriteFile");
+				bytes_written = (DWORD) -1;
+				goto end_of_function;
+			}
+		}
+
+		/* Wait here until the write is done. This makes
+		hid_write() synchronous. */
+		res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
+		if (!res) {
+			/* The Write operation failed. */
+			register_error(dev, "WriteFile");
+			bytes_written = (DWORD) -1;
+			goto end_of_function;
+		}
+	}
+end_of_function:
+	if (buf != data)
+		free(buf);
+
+	return bytes_written;
+}
+
+
+int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
+{
+	DWORD bytes_read = 0;
+	size_t copy_len = 0;
+	BOOL res;
+
+	/* Copy the handle for convenience. */
+	HANDLE ev = dev->ol.hEvent;
+
+	if (!dev->read_pending) {
+		/* Start an Overlapped I/O read. */
+		dev->read_pending = TRUE;
+		memset(dev->read_buf, 0, dev->input_report_length);
+		ResetEvent(ev);
+		res = ReadFile(dev->device_handle, dev->read_buf, (DWORD)dev->input_report_length, &bytes_read, &dev->ol);
+		
+		if (!res) {
+			if (GetLastError() != ERROR_IO_PENDING) {
+				/* ReadFile() has failed.
+				   Clean up and return error. */
+				CancelIo(dev->device_handle);
+				dev->read_pending = FALSE;
+				goto end_of_function;
+			}
+		}
+	}
+
+	if (milliseconds >= 0) {
+		/* See if there is any data yet. */
+		res = WaitForSingleObject(ev, milliseconds);
+		if (res != WAIT_OBJECT_0) {
+			/* There was no data this time. Return zero bytes available,
+			   but leave the Overlapped I/O running. */
+			return 0;
+		}
+	}
+
+	/* Either WaitForSingleObject() told us that ReadFile has completed, or
+	   we are in non-blocking mode. Get the number of bytes read. The actual
+	   data has been copied to the data[] array which was passed to ReadFile(). */
+	res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
+	
+	/* Set pending back to false, even if GetOverlappedResult() returned error. */
+	dev->read_pending = FALSE;
+
+	if (res && bytes_read > 0) {
+		if (dev->read_buf[0] == 0x0) {
+			/* If report numbers aren't being used, but Windows sticks a report
+			   number (0x0) on the beginning of the report anyway. To make this
+			   work like the other platforms, and to make it work more like the
+			   HID spec, we'll skip over this byte. */
+			bytes_read--;
+			copy_len = length > bytes_read ? bytes_read : length;
+			memcpy(data, dev->read_buf+1, copy_len);
+		}
+		else {
+			/* Copy the whole buffer, report number and all. */
+			copy_len = length > bytes_read ? bytes_read : length;
+			memcpy(data, dev->read_buf, copy_len);
+		}
+	}
+	
+end_of_function:
+	if (!res) {
+		register_error(dev, "GetOverlappedResult");
+		return -1;
+	}
+	
+	return (int)copy_len;
+}
+
+int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
+{
+	return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
+}
+
+int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
+{
+	dev->blocking = !nonblock;
+	return 0; /* Success */
+}
+
+int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
+{
+	BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, (ULONG)length);
+	if (!res) {
+		register_error(dev, "HidD_SetFeature");
+		return -1;
+	}
+
+	return (int)length;
+}
+
+
+int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
+{
+	BOOL res;
+#if 0
+	res = HidD_GetFeature(dev->device_handle, (PVOID)data, (ULONG)length);
+	if (!res) {
+		register_error(dev, "HidD_GetFeature");
+		return -1;
+	}
+	return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
+#else
+	DWORD bytes_returned;
+
+	OVERLAPPED ol;
+	memset(&ol, 0, sizeof(ol));
+
+	res = DeviceIoControl(dev->device_handle,
+		IOCTL_HID_GET_FEATURE,
+		data, (DWORD)length,
+		data, (DWORD)length,
+		&bytes_returned, &ol);
+
+	if (!res) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			/* DeviceIoControl() failed. Return error. */
+			register_error(dev, "Send Feature Report DeviceIoControl");
+			return -1;
+		}
+	}
+
+	/* Wait here until the write is done. This makes
+	   hid_get_feature_report() synchronous. */
+	res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
+	if (!res) {
+		/* The operation failed. */
+		register_error(dev, "Send Feature Report GetOverLappedResult");
+		return -1;
+	}
+
+	/* bytes_returned does not include the first byte which contains the
+	   report ID. The data buffer actually contains one more byte than
+	   bytes_returned. */
+	bytes_returned++;
+
+
+	return bytes_returned;
+#endif
+}
+
+void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
+{
+	if (!dev)
+		return;
+	CancelIo(dev->device_handle);
+	free_hid_device(dev);
+}
+
+int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	BOOL res;
+
+	res = HidD_GetManufacturerString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
+	if (!res) {
+		register_error(dev, "HidD_GetManufacturerString");
+		return -1;
+	}
+
+	return 0;
+}
+
+int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	BOOL res;
+
+	res = HidD_GetProductString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
+	if (!res) {
+		register_error(dev, "HidD_GetProductString");
+		return -1;
+	}
+
+	return 0;
+}
+
+int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
+{
+	BOOL res;
+
+	res = HidD_GetSerialNumberString(dev->device_handle, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
+	if (!res) {
+		register_error(dev, "HidD_GetSerialNumberString");
+		return -1;
+	}
+
+	return 0;
+}
+
+int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
+{
+	BOOL res;
+
+	res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)(sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)));
+	if (!res) {
+		register_error(dev, "HidD_GetIndexedString");
+		return -1;
+	}
+
+	return 0;
+}
+
+HID_API_EXPORT const wchar_t * HID_API_CALL  hid_error(hid_device *dev)
+{
+	return (wchar_t*)dev->last_error_str;
+}
+
+
+#if 0
+
+/*#define PICPGM*/
+/*#define S11*/
+#define P32
+#ifdef S11
+  unsigned short VendorID = 0xa0a0;
+	unsigned short ProductID = 0x0001;
+#endif
+
+#ifdef P32
+  unsigned short VendorID = 0x04d8;
+	unsigned short ProductID = 0x3f;
+#endif
+
+#ifdef PICPGM
+  unsigned short VendorID = 0x04d8;
+  unsigned short ProductID = 0x0033;
+#endif
+
+int __cdecl main(int argc, char* argv[])
+{
+	int res;
+	unsigned char buf[65];
+
+	UNREFERENCED_PARAMETER(argc);
+	UNREFERENCED_PARAMETER(argv);
+
+	/* Set up the command buffer. */
+	memset(buf,0x00,sizeof(buf));
+	buf[0] = 0;
+	buf[1] = 0x81;
+	
+
+	/* Open the device. */
+	int handle = open(VendorID, ProductID, L"12345");
+	if (handle < 0)
+		printf("unable to open device\n");
+
+
+	/* Toggle LED (cmd 0x80) */
+	buf[1] = 0x80;
+	res = write(handle, buf, 65);
+	if (res < 0)
+		printf("Unable to write()\n");
+
+	/* Request state (cmd 0x81) */
+	buf[1] = 0x81;
+	write(handle, buf, 65);
+	if (res < 0)
+		printf("Unable to write() (2)\n");
+
+	/* Read requested state */
+	read(handle, buf, 65);
+	if (res < 0)
+		printf("Unable to read()\n");
+
+	/* Print out the returned buffer. */
+	for (int i = 0; i < 4; i++)
+		printf("buf[%d]: %d\n", i, buf[i]);
+
+	return 0;
+}
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SDL_JOYSTICK_HIDAPI */
diff --git a/source/src/hidapi/windows/hidapi.sln b/source/src/hidapi/windows/hidapi.sln
new file mode 100644
index 0000000..af4076c
--- /dev/null
+++ b/source/src/hidapi/windows/hidapi.sln
@@ -0,0 +1,29 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "hidapi.vcproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidtest", "hidtest.vcproj", "{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
+	ProjectSection(ProjectDependencies) = postProject
+		{A107C21C-418A-4697-BB10-20C3AA60E2E4} = {A107C21C-418A-4697-BB10-20C3AA60E2E4}
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|Win32.Build.0 = Debug|Win32
+		{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.ActiveCfg = Release|Win32
+		{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|Win32.Build.0 = Release|Win32
+		{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.ActiveCfg = Debug|Win32
+		{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Debug|Win32.Build.0 = Debug|Win32
+		{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.ActiveCfg = Release|Win32
+		{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/source/src/hidapi/windows/hidapi.vcproj b/source/src/hidapi/windows/hidapi.vcproj
new file mode 100644
index 0000000..aea5a0b
--- /dev/null
+++ b/source/src/hidapi/windows/hidapi.vcproj
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="hidapi"
+	ProjectGUID="{A107C21C-418A-4697-BB10-20C3AA60E2E4}"
+	RootNamespace="hidapi"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\hidapi"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="setupapi.lib"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="..\hidapi"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;HIDAPI_EXPORTS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="setupapi.lib"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\hid.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath="..\hidapi\hidapi.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/source/src/hidapi/windows/hidtest.vcproj b/source/src/hidapi/windows/hidtest.vcproj
new file mode 100644
index 0000000..cf71195
--- /dev/null
+++ b/source/src/hidapi/windows/hidtest.vcproj
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="hidtest"
+	ProjectGUID="{23E9FF6A-49D1-4993-B2B5-BBB992C6C712}"
+	RootNamespace="hidtest"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\hidapi"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="hidapi.lib"
+				AdditionalLibraryDirectories="..\windows\Debug"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Copying hidapi.dll to the local direcotry."
+				CommandLine=""
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="1"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="..\hidapi"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="hidapi.lib"
+				AdditionalLibraryDirectories="..\windows\Release"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Copying hidapi.dll to the local direcotry."
+				CommandLine=""
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\hidtest\hidtest.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/source/src/joystick/SDL_gamecontroller.c b/source/src/joystick/SDL_gamecontroller.c
index b0f833a..eb1ef06 100644
--- a/source/src/joystick/SDL_gamecontroller.c
+++ b/source/src/joystick/SDL_gamecontroller.c
@@ -25,6 +25,7 @@
 #include "SDL_events.h"
 #include "SDL_assert.h"
 #include "SDL_hints.h"
+#include "SDL_timer.h"
 #include "SDL_sysjoystick.h"
 #include "SDL_joystick_c.h"
 #include "SDL_gamecontrollerdb.h"
@@ -37,6 +38,9 @@
 #include "SDL_system.h"
 #endif
 
+
+/* Many controllers turn the center button into an instantaneous button press */
+#define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS   250
 
 #define SDL_CONTROLLER_PLATFORM_FIELD "platform:"
 
@@ -97,8 +101,9 @@
 
 static SDL_JoystickGUID s_zeroGUID;
 static ControllerMapping_t *s_pSupportedControllers = NULL;
+static ControllerMapping_t *s_pDefaultMapping = NULL;
+static ControllerMapping_t *s_pHIDAPIMapping = NULL;
 static ControllerMapping_t *s_pXInputMapping = NULL;
-static ControllerMapping_t *s_pEmscriptenMapping = NULL;
 
 /* The SDL game controller structure */
 struct _SDL_GameController
@@ -106,12 +111,12 @@
     SDL_Joystick *joystick; /* underlying joystick device */
     int ref_count;
 
-    SDL_JoystickGUID guid;
     const char *name;
     int num_bindings;
     SDL_ExtendedGameControllerBind *bindings;
     SDL_ExtendedGameControllerBind **last_match_axis;
     Uint8 *last_hat_mask;
+    Uint32 guide_button_down;
 
     struct _SDL_GameController *next; /* pointer to next game controller we have allocated */
 };
@@ -416,7 +421,7 @@
 /*
  * Helper function to scan the mappings database for a controller with the specified GUID
  */
-static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid)
+static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid, SDL_bool exact_match)
 {
     ControllerMapping_t *pSupportedController = s_pSupportedControllers;
     while (pSupportedController) {
@@ -424,6 +429,18 @@
             return pSupportedController;
         }
         pSupportedController = pSupportedController->next;
+    }
+    if (!exact_match) {
+        if (SDL_IsJoystickHIDAPI(*guid)) {
+            /* This is a HIDAPI device */
+            return s_pHIDAPIMapping;
+        }
+#if SDL_JOYSTICK_XINPUT
+        if (SDL_IsJoystickXInput(*guid)) {
+            /* This is an XInput device */
+            return s_pXInputMapping;
+        }
+#endif
     }
     return NULL;
 }
@@ -665,11 +682,10 @@
 /*
  * Make a new button mapping struct
  */
-static void SDL_PrivateLoadButtonMapping(SDL_GameController *gamecontroller, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping)
+static void SDL_PrivateLoadButtonMapping(SDL_GameController *gamecontroller, const char *pchName, const char *pchMapping)
 {
     int i;
 
-    gamecontroller->guid = guid;
     gamecontroller->name = pchName;
     gamecontroller->num_bindings = 0;
     SDL_memset(gamecontroller->last_match_axis, 0, gamecontroller->joystick->naxes * sizeof(*gamecontroller->last_match_axis));
@@ -783,14 +799,16 @@
 {
     SDL_GameController *gamecontrollerlist = SDL_gamecontrollers;
     while (gamecontrollerlist) {
-        if (!SDL_memcmp(&gamecontrollerlist->guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid))) {
-            SDL_Event event;
-            event.type = SDL_CONTROLLERDEVICEREMAPPED;
-            event.cdevice.which = gamecontrollerlist->joystick->instance_id;
-            SDL_PushEvent(&event);
-
+        if (!SDL_memcmp(&gamecontrollerlist->joystick->guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid))) {
             /* Not really threadsafe.  Should this lock access within SDL_GameControllerEventWatcher? */
-            SDL_PrivateLoadButtonMapping(gamecontrollerlist, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping);
+            SDL_PrivateLoadButtonMapping(gamecontrollerlist, pControllerMapping->name, pControllerMapping->mapping);
+
+            {
+                SDL_Event event;
+                event.type = SDL_CONTROLLERDEVICEREMAPPED;
+                event.cdevice.which = gamecontrollerlist->joystick->instance_id;
+                SDL_PushEvent(&event);
+            }
         }
 
         gamecontrollerlist = gamecontrollerlist->next;
@@ -820,7 +838,7 @@
         return NULL;
     }
 
-    pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID);
+    pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID, SDL_TRUE);
     if (pControllerMapping) {
         /* Only overwrite the mapping if the priority is the same or higher. */
         if (pControllerMapping->priority <= priority) {
@@ -869,6 +887,119 @@
     return pControllerMapping;
 }
 
+#ifdef __ANDROID__
+/*
+ * Helper function to guess at a mapping based on the elements reported for this controller
+ */
+static ControllerMapping_t *SDL_CreateMappingForAndroidController(const char *name, SDL_JoystickGUID guid)
+{
+    SDL_bool existing;
+    char name_string[128];
+    char mapping_string[1024];
+    int button_mask;
+    int axis_mask;
+
+    button_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-4]));
+    axis_mask = SDL_SwapLE16(*(Uint16*)(&guid.data[sizeof(guid.data)-2]));
+    if (!button_mask && !axis_mask) {
+        /* Accelerometer, shouldn't have a game controller mapping */
+        return NULL;
+    }
+
+    /* Remove any commas in the name */
+    SDL_strlcpy(name_string, name, sizeof(name_string));
+    {
+        char *spot;
+        for (spot = name_string; *spot; ++spot) {
+            if (*spot == ',') {
+                *spot = ' ';
+            }
+        }
+    }
+    SDL_snprintf(mapping_string, sizeof(mapping_string), "none,%s,", name_string);
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_A)) {
+        SDL_strlcat(mapping_string, "a:b0,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_B)) {
+        SDL_strlcat(mapping_string, "b:b1,", sizeof(mapping_string));
+    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
+        /* Use the back button as "B" for easy UI navigation with TV remotes */
+        SDL_strlcat(mapping_string, "b:b4,", sizeof(mapping_string));
+        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_BACK);
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_X)) {
+        SDL_strlcat(mapping_string, "x:b2,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_Y)) {
+        SDL_strlcat(mapping_string, "y:b3,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
+        SDL_strlcat(mapping_string, "back:b4,", sizeof(mapping_string));
+    }
+#if 0 /* The guide button generally isn't functional (or acts as a home button) on most Android controllers */
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_GUIDE)) {
+        SDL_strlcat(mapping_string, "guide:b5,", sizeof(mapping_string));
+#if 0 /* Actually this will be done in Steam */
+    } else if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
+        /* The guide button doesn't exist, use the start button instead,
+           so you can do Steam guide button chords and open the Steam overlay.
+         */
+        SDL_strlcat(mapping_string, "guide:b6,", sizeof(mapping_string));
+        button_mask &= ~(1 << SDL_CONTROLLER_BUTTON_START);
+#endif
+    }
+#endif
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
+        SDL_strlcat(mapping_string, "start:b6,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
+        SDL_strlcat(mapping_string, "leftstick:b7,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
+        SDL_strlcat(mapping_string, "rightstick:b8,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) {
+        SDL_strlcat(mapping_string, "leftshoulder:b9,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) {
+        SDL_strlcat(mapping_string, "rightshoulder:b10,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_UP)) {
+        SDL_strlcat(mapping_string, "dpup:b11,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
+        SDL_strlcat(mapping_string, "dpdown:b12,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT)) {
+        SDL_strlcat(mapping_string, "dpleft:b13,", sizeof(mapping_string));
+    }
+    if (button_mask & (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
+        SDL_strlcat(mapping_string, "dpright:b14,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTX)) {
+        SDL_strlcat(mapping_string, "leftx:a0,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_LEFTY)) {
+        SDL_strlcat(mapping_string, "lefty:a1,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTX)) {
+        SDL_strlcat(mapping_string, "rightx:a2,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_RIGHTY)) {
+        SDL_strlcat(mapping_string, "righty:a3,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT)) {
+        SDL_strlcat(mapping_string, "lefttrigger:a4,", sizeof(mapping_string));
+    }
+    if (axis_mask & (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
+        SDL_strlcat(mapping_string, "righttrigger:a5,", sizeof(mapping_string));
+    }
+    return SDL_PrivateAddMappingForGUID(guid, mapping_string,
+                      &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
+}
+#endif /* __ANDROID__ */
+
+
 /*
  * Helper function to determine pre-calculated offset to certain joystick mappings
  */
@@ -876,14 +1007,7 @@
 {
     ControllerMapping_t *mapping;
 
-    mapping = SDL_PrivateGetControllerMappingForGUID(&guid);
-#if defined(SDL_JOYSTICK_EMSCRIPTEN)
-    if (!mapping && s_pEmscriptenMapping) {
-        mapping = s_pEmscriptenMapping;
-    }
-#else
-    (void) s_pEmscriptenMapping;  /* pacify ARMCC */
-#endif
+    mapping = SDL_PrivateGetControllerMappingForGUID(&guid, SDL_FALSE);
 #ifdef __LINUX__
     if (!mapping && name) {
         if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
@@ -900,6 +1024,14 @@
         if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX")) {
             mapping = s_pXInputMapping;
         }
+    }
+#ifdef __ANDROID__
+    if (!mapping && name && !SDL_IsJoystickHIDAPI(guid)) {
+        mapping = SDL_CreateMappingForAndroidController(name, guid);
+    }
+#endif
+    if (!mapping) {
+        mapping = s_pDefaultMapping;
     }
     return mapping;
 }
@@ -921,20 +1053,6 @@
     name = SDL_JoystickNameForIndex(device_index);
     guid = SDL_JoystickGetDeviceGUID(device_index);
     mapping = SDL_PrivateGetControllerMappingForNameAndGUID(name, guid);
-#if SDL_JOYSTICK_XINPUT
-    if (!mapping && SDL_SYS_IsXInputGamepad_DeviceIndex(device_index)) {
-        mapping = s_pXInputMapping;
-    }
-#endif
-#if defined(__ANDROID__)
-    if (!mapping && SDL_SYS_IsDPAD_DeviceIndex(device_index)) {
-        SDL_bool existing;
-        char mapping_string[1024];
-        SDL_snprintf(mapping_string, sizeof(mapping_string), "none,%s,a:b0,b:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,", name);
-        mapping = SDL_PrivateAddMappingForGUID(guid, mapping_string,
-                          &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT);
-    }
-#endif /* __ANDROID__ */
     SDL_UnlockJoysticks();
     return mapping;
 }
@@ -1018,8 +1136,9 @@
 {
     char *pchGUID;
     SDL_JoystickGUID jGUID;
+    SDL_bool is_default_mapping = SDL_FALSE;
+    SDL_bool is_hidapi_mapping = SDL_FALSE;
     SDL_bool is_xinput_mapping = SDL_FALSE;
-    SDL_bool is_emscripten_mapping = SDL_FALSE;
     SDL_bool existing = SDL_FALSE;
     ControllerMapping_t *pControllerMapping;
 
@@ -1031,11 +1150,12 @@
     if (!pchGUID) {
         return SDL_SetError("Couldn't parse GUID from %s", mappingString);
     }
-    if (!SDL_strcasecmp(pchGUID, "xinput")) {
+    if (!SDL_strcasecmp(pchGUID, "default")) {
+        is_default_mapping = SDL_TRUE;
+    } else if (!SDL_strcasecmp(pchGUID, "hidapi")) {
+        is_hidapi_mapping = SDL_TRUE;
+    } else if (!SDL_strcasecmp(pchGUID, "xinput")) {
         is_xinput_mapping = SDL_TRUE;
-    }
-    if (!SDL_strcasecmp(pchGUID, "emscripten")) {
-        is_emscripten_mapping = SDL_TRUE;
     }
     jGUID = SDL_JoystickGetGUIDFromString(pchGUID);
     SDL_free(pchGUID);
@@ -1048,11 +1168,12 @@
     if (existing) {
         return 0;
     } else {
-        if (is_xinput_mapping) {
+        if (is_default_mapping) {
+            s_pDefaultMapping = pControllerMapping;
+        } else if (is_hidapi_mapping) {
+            s_pHIDAPIMapping = pControllerMapping;
+        } else if (is_xinput_mapping) {
             s_pXInputMapping = pControllerMapping;
-        }
-        if (is_emscripten_mapping) {
-            s_pEmscriptenMapping = pControllerMapping;
         }
         return 1;
     }
@@ -1125,7 +1246,7 @@
 SDL_GameControllerMappingForGUID(SDL_JoystickGUID guid)
 {
     char *pMappingString = NULL;
-    ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid);
+    ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid, SDL_FALSE);
     if (mapping) {
         char pchGUID[33];
         size_t needed;
@@ -1152,7 +1273,7 @@
         return NULL;
     }
 
-    return SDL_GameControllerMappingForGUID(gamecontroller->guid);
+    return SDL_GameControllerMappingForGUID(gamecontroller->joystick->guid);
 }
 
 static void
@@ -1263,9 +1384,47 @@
 {
     ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
     if (pSupportedController) {
-        return pSupportedController->name;
+        if (SDL_strcmp(pSupportedController->name, "*") == 0) {
+            return SDL_JoystickNameForIndex(device_index);
+        } else {
+            return pSupportedController->name;
+        }
     }
     return NULL;
+}
+
+
+/**
+ *  Get the mapping of a game controller.
+ *  This can be called before any controllers are opened.
+ *  If no mapping can be found, this function returns NULL.
+ */
+char *
+SDL_GameControllerMappingForDeviceIndex(int joystick_index)
+{
+    char *pMappingString = NULL;
+    ControllerMapping_t *mapping;
+
+    SDL_LockJoysticks();
+    mapping = SDL_PrivateGetControllerMapping(joystick_index);
+    if (mapping) {
+        SDL_JoystickGUID guid;
+        char pchGUID[33];
+        size_t needed;
+        guid = SDL_JoystickGetDeviceGUID(joystick_index);
+        SDL_JoystickGetGUIDString(guid, pchGUID, sizeof(pchGUID));
+        /* allocate enough memory for GUID + ',' + name + ',' + mapping + \0 */
+        needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1;
+        pMappingString = SDL_malloc(needed);
+        if (!pMappingString) {
+            SDL_OutOfMemory();
+            SDL_UnlockJoysticks();
+            return NULL;
+        }
+        SDL_snprintf(pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping);
+    }
+    SDL_UnlockJoysticks();
+    return pMappingString;
 }
 
 
@@ -1303,6 +1462,7 @@
     int i;
     Uint16 vendor;
     Uint16 product;
+    Uint16 version;
     Uint32 vidpid;
 
     if (SDL_allowed_controllers.num_entries == 0 &&
@@ -1310,8 +1470,7 @@
         return SDL_FALSE;
     }
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
-    vidpid = MAKE_VIDPID(vendor, product);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, &version);
 
     if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", SDL_FALSE)) {
         /* We shouldn't ignore Steam's virtual gamepad since it's using the hints to filter out the real controllers so it can remap input for the virtual controller */
@@ -1319,7 +1478,7 @@
 #if defined(__LINUX__)
         bSteamVirtualGamepad = (vendor == 0x28DE && product == 0x11FF);
 #elif defined(__MACOSX__)
-        bSteamVirtualGamepad = (SDL_strncmp(name, "GamePad-", 8) == 0);
+        bSteamVirtualGamepad = (vendor == 0x045E && product == 0x028E && version == 1);
 #elif defined(__WIN32__)
         /* We can't tell on Windows, but Steam will block others in input hooks */
         bSteamVirtualGamepad = SDL_TRUE;
@@ -1328,6 +1487,8 @@
             return SDL_FALSE;
         }
     }
+
+    vidpid = MAKE_VIDPID(vendor, product);
 
     if (SDL_allowed_controllers.num_entries > 0) {
         for (i = 0; i < SDL_allowed_controllers.num_entries; ++i) {
@@ -1356,22 +1517,18 @@
 SDL_GameController *
 SDL_GameControllerOpen(int device_index)
 {
+    SDL_JoystickID instance_id;
     SDL_GameController *gamecontroller;
     SDL_GameController *gamecontrollerlist;
     ControllerMapping_t *pSupportedController = NULL;
 
     SDL_LockJoysticks();
 
-    if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
-        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
-        SDL_UnlockJoysticks();
-        return (NULL);
-    }
-
     gamecontrollerlist = SDL_gamecontrollers;
     /* If the controller is already open, return it */
+    instance_id = SDL_JoystickGetDeviceInstanceID(device_index);
     while (gamecontrollerlist) {
-        if (SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id) {
+        if (instance_id == gamecontrollerlist->joystick->instance_id) {
                 gamecontroller = gamecontrollerlist;
                 ++gamecontroller->ref_count;
                 SDL_UnlockJoysticks();
@@ -1425,7 +1582,7 @@
         }
     }
 
-    SDL_PrivateLoadButtonMapping(gamecontroller, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping);
+    SDL_PrivateLoadButtonMapping(gamecontroller, pSupportedController->name, pSupportedController->mapping);
 
     /* Add the controller to list */
     ++gamecontroller->ref_count;
@@ -1552,7 +1709,17 @@
     if (!gamecontroller)
         return NULL;
 
-    return gamecontroller->name;
+    if (SDL_strcmp(gamecontroller->name, "*") == 0) {
+        return SDL_JoystickName(SDL_GameControllerGetJoystick(gamecontroller));
+    } else {
+        return gamecontroller->name;
+    }
+}
+
+int
+SDL_GameControllerGetPlayerIndex(SDL_GameController *gamecontroller)
+{
+    return SDL_JoystickGetPlayerIndex(SDL_GameControllerGetJoystick(gamecontroller));
 }
 
 Uint16
@@ -1682,6 +1849,12 @@
     return bind;
 }
 
+
+int
+SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    return SDL_JoystickRumble(SDL_GameControllerGetJoystick(gamecontroller), low_frequency_rumble, high_frequency_rumble, duration_ms);
+}
 
 void
 SDL_GameControllerClose(SDL_GameController * gamecontroller)
@@ -1820,6 +1993,24 @@
     }
 #endif /* !SDL_EVENTS_DISABLED */
 
+    if (button == SDL_CONTROLLER_BUTTON_GUIDE) {
+        Uint32 now = SDL_GetTicks();
+        if (state == SDL_PRESSED) {
+            gamecontroller->guide_button_down = now;
+
+            if (gamecontroller->joystick->delayed_guide_button) {
+                /* Skip duplicate press */
+                return (0);
+            }
+        } else {
+            if (!SDL_TICKS_PASSED(now, gamecontroller->guide_button_down+SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS) && !gamecontroller->joystick->force_recentering) {
+                gamecontroller->joystick->delayed_guide_button = SDL_TRUE;
+                return (0);
+            }
+            gamecontroller->joystick->delayed_guide_button = SDL_FALSE;
+        }
+    }
+
     /* translate the event, if desired */
     posted = 0;
 #if !SDL_EVENTS_DISABLED
@@ -1868,4 +2059,17 @@
 #endif /* SDL_EVENTS_DISABLED */
 }
 
+void
+SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick)
+{
+    SDL_GameController *controllerlist = SDL_gamecontrollers;
+    while (controllerlist) {
+        if (controllerlist->joystick == joystick) {
+            SDL_PrivateGameControllerButton(controllerlist, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
+            break;
+        }
+        controllerlist = controllerlist->next;
+    }
+}
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/SDL_gamecontrollerdb.h b/source/src/joystick/SDL_gamecontrollerdb.h
index fdc0b59..9d95c90 100644
--- a/source/src/joystick/SDL_gamecontrollerdb.h
+++ b/source/src/joystick/SDL_gamecontrollerdb.h
@@ -35,197 +35,557 @@
     "xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
 #endif
 #if SDL_JOYSTICK_DINPUT
+    "03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,",
     "03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
     "03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+    "03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+    "03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
     "03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
     "03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+    "03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+    "030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+    "03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
     "03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,",
+    "03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,",
+    "030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+    "03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+    "030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+    "0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
     "03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
     "03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,",
     "03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
-    "03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,",
+    "030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+    "03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+    "03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+    "03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
+    "78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,",
+    "03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+    "030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+    "03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+    "030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+    "030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
     "030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,",
+    "03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,",
     "030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,",
+    "030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,",
+    "030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+    "03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+    "030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+    "030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+    "03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+    "030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,",
     "030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", /* Guide button doesn't seem to be sent in DInput mode. */
+    "03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
     "03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+    "03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
+    "03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,",
+    "03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
     "03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+    "030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,",
+    "0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+    "03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
     "030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,",
+    "03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
+    "030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,",
+    "03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,",
     "030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,",
+    "03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,",
+    "03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,",
+    "030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+    "03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,",
+    "03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+    "03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
+    "030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,",
+    "030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,",
+    "03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
     "03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,",
-    "030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
-    "03000000250900000500000000000000,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,",
+    "030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,",
+    "03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,",
+    "03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
+    "03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,",
+    "03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,",
+    "03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,",
+    "0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,",
     "03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
     "030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,",
+    "03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,",
+    "03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+    "03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+    "03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
     "03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,x:b0,y:b1,",
+    "03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
+    "03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
     "03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,",
+    "03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
+    "03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+    "03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+    "03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+    "0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,",
+    "030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,",
+    "030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,",
+    "03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+    "030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+    "030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+    "030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,",
+    "03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,",
+    "030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+    "03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
     "03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+    "03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+    "03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
     "03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
+    "030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
 #endif
 #if defined(__MACOSX__)
     "03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
     "03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
     "03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
     "03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+    "03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
     "030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,",
     "03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
-    "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000790000000600000000000000,G-Shark GP-702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
+    "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,",
-    "030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", /* Guide button doesn't seem to be sent in DInput mode. */
     "030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
     "030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", /* This includes F710 in DInput mode and the "Logitech Cordless RumblePad 2", at the very least. */
+    "03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
     "03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+    "0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+    "03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,",
     "030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,",
     "030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
     "030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+    "030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,",
     "03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
     "030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
-    "030000005e0400008e02000001000000,Steam Virtual GamePad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,",
+    "03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,",
+    "030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,",
     "03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,",
     "03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,",
     "03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,",
     "03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,",
+    "030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+    "030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,",
+    "03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,",
+    "03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+    "03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,",
+    "050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,",
+    "050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,",
     "030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
     "03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
     "030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
-    "030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
     "030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+    "030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
     "030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
     "03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
     "03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
+    "03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,",
 #endif
 #if defined(__LINUX__)
+    "03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
     "03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
     "05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+    "05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+    "05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
     "05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
+    "05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
+    "05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,",
     "05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+    "05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,",
+    "030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,",
     "03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
     "03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,",
     "03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
     "03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
-    "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+    "03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+    "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
     "030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,",
+    "030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+    "03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+    "030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,",
     "030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,",
-    "03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
-    "030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
-    "030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,",
+    "030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+    "03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+    "03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", /* Guide button doesn't seem to be sent in DInput mode. */
     "030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,",
     "030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,",
+    "03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
-    "03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,",
     "03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
     "03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+    "03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+    "030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,",
+    "030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+    "030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+    "05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
     "030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b6,start:b9,x:b3,y:b0,",
     "03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+    "03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
     "050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+    "05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
     "030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,",
     "05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
+    "05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
+    "030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+    "03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
     "030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
-    "030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
-    "050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
-    "030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
-    "030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
-    "050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
-    "030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
     "03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
-    "030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+    "030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+    "030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+    "050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+    "060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
     "030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
-    "030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
-    "050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
-    "030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
-    "050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
-    "050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
     "030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+    "03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,",
+    "030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
     "03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
     "050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
     "0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,",
     "03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
     "030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
     "03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,",
+    "03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,",
+    "03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,",
     "03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,",
-    "03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
-    "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+    "03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+    "03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,",
+    "03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+    "03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
-    "05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+    "03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
     "03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
-    "03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+    "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+    "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
     "03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+    "0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,",
+    "030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+    "030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+    "030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,",
+    "030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+    "03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,",
+    "03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+    "03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,",
+    "03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
+    "05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+    "030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
     "xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+    "030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+    "0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
     "050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     "050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
     "05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+    "03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,",
     "03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+    "03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
+    "030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,",
     "03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
+    "050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+    "03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+    "030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,",
 #endif
 #if defined(__ANDROID__)
-    "34323662653333636330306631326233,ASUS Gamepad,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
-    "64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
-    "4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
-    "61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
-    "37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
-    "35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+    "05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+    "05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+    "050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+    "050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+    "050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,", /* Extremely slow in Bluetooth mode on Android */
+    "050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+    "050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+    "050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+    "050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
     "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
-    "34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,",
+    "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+    "050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,",
+    "050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+    "050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,", /* The DPAD doesn't seem to work on this controller on Android TV? */
 #endif
 #if defined(SDL_JOYSTICK_MFI)
-    "4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
-    "4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,",
-    "4d466947616d65706164030000000000,Remote,a:b0,b:b2,leftx:a0,lefty:a1,",
+    "05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,",
+    "05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,",
+    "05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,",
     "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+    "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
 #endif
 #if defined(SDL_JOYSTICK_EMSCRIPTEN)
-    "emscripten,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "default,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
 #endif
+    "hidapi,*,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
     NULL
 };
 
diff --git a/source/src/joystick/SDL_joystick.c b/source/src/joystick/SDL_joystick.c
index b93c03d..02903f5 100644
--- a/source/src/joystick/SDL_joystick.c
+++ b/source/src/joystick/SDL_joystick.c
@@ -23,6 +23,7 @@
 /* This is the joystick API for Simple DirectMedia Layer */
 
 #include "SDL.h"
+#include "SDL_atomic.h"
 #include "SDL_events.h"
 #include "SDL_sysjoystick.h"
 #include "SDL_assert.h"
@@ -33,11 +34,54 @@
 #endif
 #include "../video/SDL_sysvideo.h"
 
+/* This is included in only one place because it has a large static list of controllers */
+#include "controller_type.h"
 
+#ifdef __WIN32__
+/* Needed for checking for input remapping programs */
+#include "../core/windows/SDL_windows.h"
+
+#undef UNICODE          /* We want ASCII functions */
+#include <tlhelp32.h>
+#endif
+
+static SDL_JoystickDriver *SDL_joystick_drivers[] = {
+#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
+    &SDL_WINDOWS_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_LINUX
+    &SDL_LINUX_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_IOKIT
+    &SDL_DARWIN_JoystickDriver,
+#endif
+#if defined(__IPHONEOS__) || defined(__TVOS__)
+    &SDL_IOS_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_ANDROID
+    &SDL_ANDROID_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_EMSCRIPTEN
+    &SDL_EMSCRIPTEN_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_HAIKU
+    &SDL_HAIKU_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_USBHID  /* !!! FIXME: "USBHID" is a generic name, and doubly-confusing with HIDAPI next to it. This is the *BSD interface, rename this. */
+    &SDL_BSD_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI
+    &SDL_HIDAPI_JoystickDriver,
+#endif
+#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
+    &SDL_DUMMY_JoystickDriver
+#endif
+};
 static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
 static SDL_Joystick *SDL_joysticks = NULL;
 static SDL_bool SDL_updating_joystick = SDL_FALSE;
 static SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */
+static SDL_atomic_t SDL_next_joystick_instance_id;
 
 void
 SDL_LockJoysticks(void)
@@ -69,7 +113,7 @@
 int
 SDL_JoystickInit(void)
 {
-    int status;
+    int i, status;
 
     SDL_GameControllerInitMappings();
 
@@ -88,11 +132,13 @@
     }
 #endif /* !SDL_EVENTS_DISABLED */
 
-    status = SDL_SYS_JoystickInit();
-    if (status >= 0) {
-        status = 0;
+    status = -1;
+    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+        if (SDL_joystick_drivers[i]->Init() >= 0) {
+            status = 0;
+        }
     }
-    return (status);
+    return status;
 }
 
 /*
@@ -101,8 +147,66 @@
 int
 SDL_NumJoysticks(void)
 {
-    return SDL_SYS_NumJoysticks();
+    int i, total_joysticks = 0;
+    SDL_LockJoysticks();
+    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+        total_joysticks += SDL_joystick_drivers[i]->GetCount();
+    }
+    SDL_UnlockJoysticks();
+    return total_joysticks;
 }
+
+/*
+ * Return the next available joystick instance ID
+ * This may be called by drivers from multiple threads, unprotected by any locks
+ */
+SDL_JoystickID SDL_GetNextJoystickInstanceID()
+{
+    return SDL_AtomicIncRef(&SDL_next_joystick_instance_id);
+}
+
+/*
+ * Get the driver and device index for an API device index
+ * This should be called while the joystick lock is held, to prevent another thread from updating the list
+ */
+SDL_bool
+SDL_GetDriverAndJoystickIndex(int device_index, SDL_JoystickDriver **driver, int *driver_index)
+{
+    int i, num_joysticks, total_joysticks = 0;
+
+    if (device_index >= 0) {
+        for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+            num_joysticks = SDL_joystick_drivers[i]->GetCount();
+            if (device_index < num_joysticks) {
+                *driver = SDL_joystick_drivers[i];
+                *driver_index = device_index;
+                return SDL_TRUE;
+            }
+            device_index -= num_joysticks;
+            total_joysticks += num_joysticks;
+        }
+    }
+
+    SDL_SetError("There are %d joysticks available", total_joysticks);
+    return SDL_FALSE;
+}
+
+/*
+ * Perform any needed fixups for joystick names
+ */
+static const char *
+SDL_FixupJoystickName(const char *name)
+{
+    if (name) {
+        const char *skip_prefix = "NVIDIA Corporation ";
+
+        if (SDL_strncmp(name, skip_prefix, SDL_strlen(skip_prefix)) == 0) {
+            name += SDL_strlen(skip_prefix);
+        }
+    }
+    return name;
+}
+
 
 /*
  * Get the implementation dependent name of a joystick
@@ -110,11 +214,32 @@
 const char *
 SDL_JoystickNameForIndex(int device_index)
 {
-    if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
-        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
-        return (NULL);
+    SDL_JoystickDriver *driver;
+    const char *name = NULL;
+
+    SDL_LockJoysticks();
+    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+        name = SDL_FixupJoystickName(driver->GetDeviceName(device_index));
     }
-    return (SDL_SYS_JoystickNameForDeviceIndex(device_index));
+    SDL_UnlockJoysticks();
+
+    /* FIXME: Really we should reference count this name so it doesn't go away after unlock */
+    return name;
+}
+
+int
+SDL_JoystickGetDevicePlayerIndex(int device_index)
+{
+    SDL_JoystickDriver *driver;
+    int player_index = -1;
+
+    SDL_LockJoysticks();
+    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+        player_index = driver->GetDevicePlayerIndex(device_index);
+    }
+    SDL_UnlockJoysticks();
+
+    return player_index;
 }
 
 /*
@@ -159,27 +284,30 @@
 SDL_Joystick *
 SDL_JoystickOpen(int device_index)
 {
+    SDL_JoystickDriver *driver;
+    SDL_JoystickID instance_id;
     SDL_Joystick *joystick;
     SDL_Joystick *joysticklist;
     const char *joystickname = NULL;
 
-    if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
-        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
-        return (NULL);
-    }
-
     SDL_LockJoysticks();
+
+    if (!SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+        SDL_UnlockJoysticks();
+        return NULL;
+    }
 
     joysticklist = SDL_joysticks;
     /* If the joystick is already open, return it
      * it is important that we have a single joystick * for each instance id
      */
+    instance_id = driver->GetDeviceInstanceID(device_index);
     while (joysticklist) {
-        if (SDL_JoystickGetDeviceInstanceID(device_index) == joysticklist->instance_id) {
+        if (instance_id == joysticklist->instance_id) {
                 joystick = joysticklist;
                 ++joystick->ref_count;
                 SDL_UnlockJoysticks();
-                return (joystick);
+                return joystick;
         }
         joysticklist = joysticklist->next;
     }
@@ -191,18 +319,25 @@
         SDL_UnlockJoysticks();
         return NULL;
     }
+    joystick->driver = driver;
+    joystick->instance_id = instance_id;
+    joystick->attached = SDL_TRUE;
+    joystick->player_index = -1;
 
-    if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) {
+    if (driver->Open(joystick, device_index) < 0) {
         SDL_free(joystick);
         SDL_UnlockJoysticks();
         return NULL;
     }
 
-    joystickname = SDL_SYS_JoystickNameForDeviceIndex(device_index);
-    if (joystickname)
+    joystickname = driver->GetDeviceName(device_index);
+    if (joystickname) {
         joystick->name = SDL_strdup(joystickname);
-    else
+    } else {
         joystick->name = NULL;
+    }
+
+    joystick->guid = driver->GetDeviceGUID(device_index);
 
     if (joystick->naxes > 0) {
         joystick->axes = (SDL_JoystickAxisInfo *) SDL_calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
@@ -246,9 +381,9 @@
 
     SDL_UnlockJoysticks();
 
-    SDL_SYS_JoystickUpdate(joystick);
+    driver->Update(joystick);
 
-    return (joystick);
+    return joystick;
 }
 
 
@@ -277,9 +412,9 @@
 SDL_JoystickNumAxes(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (-1);
+        return -1;
     }
-    return (joystick->naxes);
+    return joystick->naxes;
 }
 
 /*
@@ -289,9 +424,9 @@
 SDL_JoystickNumHats(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (-1);
+        return -1;
     }
-    return (joystick->nhats);
+    return joystick->nhats;
 }
 
 /*
@@ -301,9 +436,9 @@
 SDL_JoystickNumBalls(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (-1);
+        return -1;
     }
-    return (joystick->nballs);
+    return joystick->nballs;
 }
 
 /*
@@ -313,9 +448,9 @@
 SDL_JoystickNumButtons(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (-1);
+        return -1;
     }
-    return (joystick->nbuttons);
+    return joystick->nbuttons;
 }
 
 /*
@@ -327,7 +462,7 @@
     Sint16 state;
 
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (0);
+        return 0;
     }
     if (axis < joystick->naxes) {
         state = joystick->axes[axis].value;
@@ -335,7 +470,7 @@
         SDL_SetError("Joystick only has %d axes", joystick->naxes);
         state = 0;
     }
-    return (state);
+    return state;
 }
 
 /*
@@ -366,7 +501,7 @@
     Uint8 state;
 
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (0);
+        return 0;
     }
     if (hat < joystick->nhats) {
         state = joystick->hats[hat];
@@ -374,7 +509,7 @@
         SDL_SetError("Joystick only has %d hats", joystick->nhats);
         state = 0;
     }
-    return (state);
+    return state;
 }
 
 /*
@@ -386,7 +521,7 @@
     int retval;
 
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (-1);
+        return -1;
     }
 
     retval = 0;
@@ -402,7 +537,7 @@
     } else {
         return SDL_SetError("Joystick only has %d balls", joystick->nballs);
     }
-    return (retval);
+    return retval;
 }
 
 /*
@@ -414,7 +549,7 @@
     Uint8 state;
 
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (0);
+        return 0;
     }
     if (button < joystick->nbuttons) {
         state = joystick->buttons[button];
@@ -422,7 +557,7 @@
         SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
         state = 0;
     }
-    return (state);
+    return state;
 }
 
 /*
@@ -436,7 +571,7 @@
         return SDL_FALSE;
     }
 
-    return SDL_SYS_JoystickAttached(joystick);
+    return joystick->attached;
 }
 
 /*
@@ -446,10 +581,10 @@
 SDL_JoystickInstanceID(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (-1);
+        return -1;
     }
 
-    return (joystick->instance_id);
+    return joystick->instance_id;
 }
 
 /*
@@ -463,12 +598,11 @@
     SDL_LockJoysticks();
     for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
         if (joystick->instance_id == joyid) {
-            SDL_UnlockJoysticks();
-            return joystick;
+            break;
         }
     }
     SDL_UnlockJoysticks();
-    return NULL;
+    return joystick;
 }
 
 /*
@@ -478,10 +612,28 @@
 SDL_JoystickName(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (NULL);
+        return NULL;
     }
 
-    return (joystick->name);
+    return SDL_FixupJoystickName(joystick->name);
+}
+
+int
+SDL_JoystickGetPlayerIndex(SDL_Joystick * joystick)
+{
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return -1;
+    }
+    return joystick->player_index;
+}
+
+int
+SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return -1;
+    }
+    return joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
 }
 
 /*
@@ -493,7 +645,7 @@
     SDL_Joystick *joysticklist;
     SDL_Joystick *joysticklistprev;
 
-    if (!joystick) {
+    if (!SDL_PrivateJoystickValid(joystick)) {
         return;
     }
 
@@ -510,7 +662,7 @@
         return;
     }
 
-    SDL_SYS_JoystickClose(joystick);
+    joystick->driver->Close(joystick);
     joystick->hwdata = NULL;
 
     joysticklist = SDL_joysticks;
@@ -544,6 +696,8 @@
 void
 SDL_JoystickQuit(void)
 {
+    int i;
+
     /* Make sure we're not getting called in the middle of updating joysticks */
     SDL_assert(!SDL_updating_joystick);
 
@@ -556,7 +710,9 @@
     }
 
     /* Quit the joystick setup */
-    SDL_SYS_JoystickQuit();
+    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+       SDL_joystick_drivers[i]->Quit();
+    }
 
     SDL_UnlockJoysticks();
 
@@ -592,10 +748,16 @@
 
 /* These are global for SDL_sysjoystick.c and SDL_events.c */
 
-void SDL_PrivateJoystickAdded(int device_index)
+void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
 {
 #if !SDL_EVENTS_DISABLED
     SDL_Event event;
+    int device_index;
+
+    device_index = SDL_JoystickGetDeviceIndexFromInstanceID(device_instance);
+    if (device_index < 0) {
+        return;
+    }
 
     event.type = SDL_JOYDEVICEADDED;
 
@@ -637,6 +799,8 @@
 
 void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
 {
+    SDL_Joystick *joystick;
+
 #if !SDL_EVENTS_DISABLED
     SDL_Event event;
 
@@ -649,6 +813,15 @@
 
     UpdateEventsForDeviceRemoval();
 #endif /* !SDL_EVENTS_DISABLED */
+
+    /* Mark this joystick as no longer attached */
+    for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
+        if (joystick->instance_id == device_instance) {
+            joystick->attached = SDL_FALSE;
+            joystick->force_recentering = SDL_TRUE;
+            break;
+        }
+    }
 }
 
 int
@@ -705,7 +878,7 @@
         posted = SDL_PushEvent(&event) == 1;
     }
 #endif /* !SDL_EVENTS_DISABLED */
-    return (posted);
+    return posted;
 }
 
 int
@@ -745,7 +918,7 @@
         posted = SDL_PushEvent(&event) == 1;
     }
 #endif /* !SDL_EVENTS_DISABLED */
-    return (posted);
+    return posted;
 }
 
 int
@@ -781,7 +954,7 @@
         posted = SDL_PushEvent(&event) == 1;
     }
 #endif /* !SDL_EVENTS_DISABLED */
-    return (posted);
+    return posted;
 }
 
 int
@@ -800,7 +973,7 @@
         break;
     default:
         /* Invalid state -- bail */
-        return (0);
+        return 0;
     }
 #endif /* !SDL_EVENTS_DISABLED */
 
@@ -833,12 +1006,13 @@
         posted = SDL_PushEvent(&event) == 1;
     }
 #endif /* !SDL_EVENTS_DISABLED */
-    return (posted);
+    return posted;
 }
 
 void
 SDL_JoystickUpdate(void)
 {
+    int i;
     SDL_Joystick *joystick;
 
     SDL_LockJoysticks();
@@ -855,11 +1029,15 @@
     SDL_UnlockJoysticks();
 
     for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
-        SDL_SYS_JoystickUpdate(joystick);
+        if (joystick->attached) {
+            joystick->driver->Update(joystick);
+
+            if (joystick->delayed_guide_button) {
+                SDL_GameControllerHandleDelayedGuideButton(joystick);
+            }
+        }
 
         if (joystick->force_recentering) {
-            int i;
-
             /* Tell the app that everything is centered/unpressed... */
             for (i = 0; i < joystick->naxes; i++) {
                 if (joystick->axes[i].has_initial_value) {
@@ -893,7 +1071,9 @@
     /* this needs to happen AFTER walking the joystick list above, so that any
        dangling hardware data from removed devices can be free'd
      */
-    SDL_SYS_JoystickDetect();
+    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+        SDL_joystick_drivers[i]->Detect();
+    }
 
     SDL_UnlockJoysticks();
 }
@@ -926,7 +1106,7 @@
         }
         break;
     }
-    return (state);
+    return state;
 #endif /* SDL_EVENTS_DISABLED */
 }
 
@@ -942,7 +1122,7 @@
         /* guid16[4] is product ID */
         guid16[5] == 0x0000
         /* guid16[6] is product version */
-    ) {
+   ) {
         if (vendor) {
             *vendor = guid16[2];
         }
@@ -963,6 +1143,55 @@
             *version = 0;
         }
     }
+}
+
+SDL_bool
+SDL_IsJoystickPS4(Uint16 vendor, Uint16 product)
+{
+    return (GuessControllerType(vendor, product) == k_eControllerType_PS4Controller);
+}
+
+SDL_bool
+SDL_IsJoystickNintendoSwitchPro(Uint16 vendor, Uint16 product)
+{
+    return (GuessControllerType(vendor, product) == k_eControllerType_SwitchProController);
+}
+
+SDL_bool
+SDL_IsJoystickSteamController(Uint16 vendor, Uint16 product)
+{
+    return BIsSteamController(GuessControllerType(vendor, product));
+}
+
+SDL_bool
+SDL_IsJoystickXbox360(Uint16 vendor, Uint16 product)
+{
+    /* Filter out some bogus values here */
+    if (vendor == 0x0000 && product == 0x0000) {
+        return SDL_FALSE;
+    }
+    if (vendor == 0x0001 && product == 0x0001) {
+        return SDL_FALSE;
+    }
+    return (GuessControllerType(vendor, product) == k_eControllerType_XBox360Controller);
+}
+
+SDL_bool
+SDL_IsJoystickXboxOne(Uint16 vendor, Uint16 product)
+{
+    return (GuessControllerType(vendor, product) == k_eControllerType_XBoxOneController);
+}
+
+SDL_bool
+SDL_IsJoystickXInput(SDL_JoystickGUID guid)
+{
+    return (guid.data[14] == 'x') ? SDL_TRUE : SDL_FALSE;
+}
+
+SDL_bool
+SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid)
+{
+    return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE;
 }
 
 static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
@@ -1030,7 +1259,7 @@
     Uint16 product;
     Uint32 vidpid;
 
-    if (guid.data[14] == 'x') {
+    if (SDL_IsJoystickXInput(guid)) {
         /* XInput GUID, get the type based on the XInput device subtype */
         switch (guid.data[15]) {
         case 0x01:  /* XINPUT_DEVSUBTYPE_GAMEPAD */
@@ -1071,19 +1300,80 @@
         return SDL_JOYSTICK_TYPE_THROTTLE;
     }
 
+    if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
+        return SDL_JOYSTICK_TYPE_GAMECONTROLLER;
+    }
+
     return SDL_JOYSTICK_TYPE_UNKNOWN;
+}
+
+static SDL_bool SDL_IsPS4RemapperRunning(void)
+{
+#ifdef __WIN32__
+    const char *mapper_processes[] = {
+        "DS4Windows.exe",
+        "InputMapper.exe",
+    };
+    int i;
+    PROCESSENTRY32 pe32;
+    SDL_bool found = SDL_FALSE;
+
+    /* Take a snapshot of all processes in the system */
+    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    if (hProcessSnap != INVALID_HANDLE_VALUE) {
+        pe32.dwSize = sizeof(PROCESSENTRY32);
+        if (Process32First(hProcessSnap, &pe32)) {
+            do
+            {
+                for (i = 0; i < SDL_arraysize(mapper_processes); ++i) {
+                    if (SDL_strcasecmp(pe32.szExeFile, mapper_processes[i]) == 0) {
+                        found = SDL_TRUE;
+                    }
+                }
+            } while (Process32Next(hProcessSnap, &pe32) && !found);
+        }
+        CloseHandle(hProcessSnap);
+    }
+    return found;
+#else
+    return SDL_FALSE;
+#endif
+}
+
+SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
+{
+    Uint16 vendor;
+    Uint16 product;
+
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+
+    if (SDL_IsJoystickPS4(vendor, product) && SDL_IsPS4RemapperRunning()) {
+        return SDL_TRUE;
+    }
+
+    if (SDL_IsGameControllerNameAndGUID(name, guid) &&
+        SDL_ShouldIgnoreGameController(name, guid)) {
+        return SDL_TRUE;
+    }
+
+    return SDL_FALSE;
 }
 
 /* return the guid for this index */
 SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
 {
-    if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
-        SDL_JoystickGUID emptyGUID;
-        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
-        SDL_zero(emptyGUID);
-        return emptyGUID;
+    SDL_JoystickDriver *driver;
+    SDL_JoystickGUID guid;
+
+    SDL_LockJoysticks();
+    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+        guid = driver->GetDeviceGUID(device_index);
+    } else {
+        SDL_zero(guid);
     }
-    return SDL_SYS_JoystickGetDeviceGUID(device_index);
+    SDL_UnlockJoysticks();
+
+    return guid;
 }
 
 Uint16 SDL_JoystickGetDeviceVendor(int device_index)
@@ -1129,11 +1419,33 @@
 
 SDL_JoystickID SDL_JoystickGetDeviceInstanceID(int device_index)
 {
-    if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
-        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
-        return -1;
+    SDL_JoystickDriver *driver;
+    SDL_JoystickID instance_id = -1;
+
+    SDL_LockJoysticks();
+    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+        instance_id = driver->GetDeviceInstanceID(device_index);
     }
-    return SDL_SYS_GetInstanceIdOfDeviceIndex(device_index);
+    SDL_UnlockJoysticks();
+
+    return instance_id;
+}
+
+int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id)
+{
+    int i, num_joysticks, device_index = -1;
+
+    SDL_LockJoysticks();
+    num_joysticks = SDL_NumJoysticks();
+    for (i = 0; i < num_joysticks; ++i) {
+        if (SDL_JoystickGetDeviceInstanceID(i) == instance_id) {
+            device_index = i;
+            break;
+        }
+    }
+    SDL_UnlockJoysticks();
+
+    return device_index;
 }
 
 SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
@@ -1143,7 +1455,7 @@
         SDL_zero(emptyGUID);
         return emptyGUID;
     }
-    return SDL_SYS_JoystickGetGUID(joystick);
+    return joystick->guid;
 }
 
 Uint16 SDL_JoystickGetVendor(SDL_Joystick * joystick)
@@ -1208,7 +1520,6 @@
     *pszGUID = '\0';
 }
 
-
 /*-----------------------------------------------------------------------------
  * Purpose: Returns the 4 bit nibble for a hex character
  * Input  : c -
@@ -1233,7 +1544,6 @@
     return 0;
 }
 
-
 /* convert the string version of a joystick guid to the struct */
 SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
 {
@@ -1256,19 +1566,17 @@
     return guid;
 }
 
-
 /* update the power level for this joystick */
 void SDL_PrivateJoystickBatteryLevel(SDL_Joystick * joystick, SDL_JoystickPowerLevel ePowerLevel)
 {
     joystick->epowerlevel = ePowerLevel;
 }
 
-
 /* return its power level */
 SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
 {
     if (!SDL_PrivateJoystickValid(joystick)) {
-        return (SDL_JOYSTICK_POWER_UNKNOWN);
+        return SDL_JOYSTICK_POWER_UNKNOWN;
     }
     return joystick->epowerlevel;
 }
diff --git a/source/src/joystick/SDL_joystick_c.h b/source/src/joystick/SDL_joystick_c.h
index 0a8fdb4..900d590 100644
--- a/source/src/joystick/SDL_joystick_c.h
+++ b/source/src/joystick/SDL_joystick_c.h
@@ -18,14 +18,23 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_joystick_c_h_
+#define SDL_joystick_c_h_
+
 #include "../SDL_internal.h"
 
 /* Useful functions and variables from SDL_joystick.c */
 #include "SDL_joystick.h"
 
+struct _SDL_JoystickDriver;
+
 /* Initialization and shutdown functions */
 extern int SDL_JoystickInit(void);
 extern void SDL_JoystickQuit(void);
+
+/* Function to get the next available joystick instance ID */
+extern SDL_JoystickID SDL_GetNextJoystickInstanceID(void);
 
 /* Initialization and shutdown functions */
 extern int SDL_GameControllerInitMappings(void);
@@ -33,8 +42,38 @@
 extern int SDL_GameControllerInit(void);
 extern void SDL_GameControllerQuit(void);
 
+/* Function to get the joystick driver and device index for an API device index */
+extern SDL_bool SDL_GetDriverAndJoystickIndex(int device_index, struct _SDL_JoystickDriver **driver, int *driver_index);
+
+/* Function to return the device index for a joystick ID, or -1 if not found */
+extern int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id);
+
 /* Function to extract information from an SDL joystick GUID */
 extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version);
+
+/* Function to return whether a joystick is a PS4 controller */
+extern SDL_bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is a Nintendo Switch Pro controller */
+extern SDL_bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is a Steam Controller */
+extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is an Xbox 360 controller */
+extern SDL_bool SDL_IsJoystickXbox360(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is an Xbox One controller */
+extern SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick guid comes from the XInput driver */
+extern SDL_bool SDL_IsJoystickXInput(SDL_JoystickGUID guid);
+
+/* Function to return whether a joystick guid comes from the HIDAPI driver */
+extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid);
+
+/* Function to return whether a joystick should be ignored */
+extern SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid);
 
 /* Function to return whether a joystick name and GUID is a game controller  */
 extern SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid);
@@ -42,8 +81,11 @@
 /* Function to return whether a game controller should be ignored */
 extern SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid);
 
+/* Handle delayed guide button on a game controller */
+extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick);
+
 /* Internal event queueing functions */
-extern void SDL_PrivateJoystickAdded(int device_index);
+extern void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance);
 extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance);
 extern int SDL_PrivateJoystickAxis(SDL_Joystick * joystick,
                                    Uint8 axis, Sint16 value);
@@ -59,4 +101,6 @@
 /* Internal sanity checking functions */
 extern int SDL_PrivateJoystickValid(SDL_Joystick * joystick);
 
+#endif /* SDL_joystick_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/SDL_sysjoystick.h b/source/src/joystick/SDL_sysjoystick.h
index 7de5d83..3416693 100644
--- a/source/src/joystick/SDL_sysjoystick.h
+++ b/source/src/joystick/SDL_sysjoystick.h
@@ -42,6 +42,8 @@
 {
     SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
     char *name;                 /* Joystick name - system dependent */
+    int player_index;           /* Joystick player index, or -1 if unavailable */
+    SDL_JoystickGUID guid;      /* Joystick guid */
 
     int naxes;                  /* Number of axis controls on the joystick */
     SDL_JoystickAxisInfo *axes;
@@ -58,78 +60,97 @@
     int nbuttons;               /* Number of buttons on the joystick */
     Uint8 *buttons;             /* Current button states */
 
+    SDL_bool attached;
+    SDL_bool is_game_controller;
+    SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */
+    SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
+    SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
+    struct _SDL_JoystickDriver *driver;
+
     struct joystick_hwdata *hwdata;     /* Driver dependent information */
 
     int ref_count;              /* Reference count for multiple opens */
 
-    SDL_bool is_game_controller;
-    SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
-    SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
     struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
 };
+
+#if defined(__IPHONEOS__) || defined(__ANDROID__)
+#define HAVE_STEAMCONTROLLERS
+#define USE_STEAMCONTROLLER_HIDAPI
+#elif defined(__LINUX__)
+#define HAVE_STEAMCONTROLLERS
+#define USE_STEAMCONTROLLER_LINUX
+#endif
+
+/* Device bus definitions */
+#define SDL_HARDWARE_BUS_USB        0x03
+#define SDL_HARDWARE_BUS_BLUETOOTH  0x05
 
 /* Macro to combine a USB vendor ID and product ID into a single Uint32 value */
 #define MAKE_VIDPID(VID, PID)   (((Uint32)(VID))<<16|(PID))
 
-/* Function to scan the system for joysticks.
- * Joystick 0 should be the system default joystick.
- * This function should return the number of available joysticks, or -1
- * on an unrecoverable fatal error.
- */
-extern int SDL_SYS_JoystickInit(void);
+typedef struct _SDL_JoystickDriver
+{
+    /* Function to scan the system for joysticks.
+     * Joystick 0 should be the system default joystick.
+     * This function should return 0, or -1 on an unrecoverable error.
+     */
+    int (*Init)(void);
 
-/* Function to return the number of joystick devices plugged in right now */
-extern int SDL_SYS_NumJoysticks(void);
+    /* Function to return the number of joystick devices plugged in right now */
+    int (*GetCount)(void);
 
-/* Function to cause any queued joystick insertions to be processed */
-extern void SDL_SYS_JoystickDetect(void);
+    /* Function to cause any queued joystick insertions to be processed */
+    void (*Detect)(void);
 
-/* Function to get the device-dependent name of a joystick */
-extern const char *SDL_SYS_JoystickNameForDeviceIndex(int device_index);
+    /* Function to get the device-dependent name of a joystick */
+    const char *(*GetDeviceName)(int device_index);
 
-/* Function to get the current instance id of the joystick located at device_index */
-extern SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index);
+    /* Function to get the player index of a joystick */
+    int (*GetDevicePlayerIndex)(int device_index);
 
-/* Function to open a joystick for use.
-   The joystick to open is specified by the device index.
-   This should fill the nbuttons and naxes fields of the joystick structure.
-   It returns 0, or -1 if there is an error.
- */
-extern int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index);
+    /* Function to return the stable GUID for a plugged in device */
+    SDL_JoystickGUID (*GetDeviceGUID)(int device_index);
 
-/* Function to query if the joystick is currently attached
- * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
- */
-extern SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick * joystick);
+    /* Function to get the current instance id of the joystick located at device_index */
+    SDL_JoystickID (*GetDeviceInstanceID)(int device_index);
 
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-extern void SDL_SYS_JoystickUpdate(SDL_Joystick * joystick);
+    /* Function to open a joystick for use.
+       The joystick to open is specified by the device index.
+       This should fill the nbuttons and naxes fields of the joystick structure.
+       It returns 0, or -1 if there is an error.
+     */
+    int (*Open)(SDL_Joystick * joystick, int device_index);
 
-/* Function to close a joystick after use */
-extern void SDL_SYS_JoystickClose(SDL_Joystick * joystick);
+    /* Rumble functionality */
+    int (*Rumble)(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 
-/* Function to perform any system-specific joystick related cleanup */
-extern void SDL_SYS_JoystickQuit(void);
+    /* Function to update the state of a joystick - called as a device poll.
+     * This function shouldn't update the joystick structure directly,
+     * but instead should call SDL_PrivateJoystick*() to deliver events
+     * and update joystick device state.
+     */
+    void (*Update)(SDL_Joystick * joystick);
 
-/* Function to return the stable GUID for a plugged in device */
-extern SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index);
+    /* Function to close a joystick after use */
+    void (*Close)(SDL_Joystick * joystick);
 
-/* Function to return the stable GUID for a opened joystick */
-extern SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick);
+    /* Function to perform any system-specific joystick related cleanup */
+    void (*Quit)(void);
 
-#if SDL_JOYSTICK_XINPUT
-/* Function returns SDL_TRUE if this device is an XInput gamepad */
-extern SDL_bool SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index);
-#endif
+} SDL_JoystickDriver;
 
-#if defined(__ANDROID__)
-/* Function returns SDL_TRUE if this device is a DPAD (maybe a TV remote) */
-extern SDL_bool SDL_SYS_IsDPAD_DeviceIndex(int device_index);
-#endif
+/* The available joystick drivers */
+extern SDL_JoystickDriver SDL_ANDROID_JoystickDriver;
+extern SDL_JoystickDriver SDL_BSD_JoystickDriver;
+extern SDL_JoystickDriver SDL_DARWIN_JoystickDriver;
+extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver;
+extern SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver;
+extern SDL_JoystickDriver SDL_HAIKU_JoystickDriver;
+extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
+extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
+extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
+extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver;
 
 #endif /* SDL_sysjoystick_h_ */
 
diff --git a/source/src/joystick/android/SDL_sysjoystick.c b/source/src/joystick/android/SDL_sysjoystick.c
index ce5a5df..69b657f 100644
--- a/source/src/joystick/android/SDL_sysjoystick.c
+++ b/source/src/joystick/android/SDL_sysjoystick.c
@@ -36,7 +36,7 @@
 #include "../SDL_joystick_c.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../core/android/SDL_android.h"
-#include "../steam/SDL_steamcontroller.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
 
 #include "android/keycodes.h"
 
@@ -69,8 +69,29 @@
 static SDL_joylist_item *SDL_joylist = NULL;
 static SDL_joylist_item *SDL_joylist_tail = NULL;
 static int numjoysticks = 0;
-static int instance_counter = 0;
 
+
+/* Public domain CRC implementation adapted from:
+   http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
+*/
+static Uint32 crc32_for_byte(Uint32 r)
+{
+    int i;
+    for(i = 0; i < 8; ++i) {
+        r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
+    }
+    return r ^ (Uint32)0xFF000000L;
+}
+
+static Uint32 crc32(const void *data, int count)
+{
+    Uint32 crc = 0;
+    int i;
+    for(i = 0; i < count; ++i) {
+        crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
+    }
+    return crc;
+}
 
 /* Function to convert Android keyCodes into SDL ones.
  * This code manipulation is done to get a sequential list of codes.
@@ -213,7 +234,7 @@
     if (button >= 0) {
         item = JoystickByDeviceId(device_id);
         if (item && item->joystick) {
-            SDL_PrivateJoystickButton(item->joystick, button , SDL_PRESSED);
+            SDL_PrivateJoystickButton(item->joystick, button, SDL_PRESSED);
         } else {
             SDL_SendKeyboardKey(SDL_PRESSED, button_to_scancode(button));
         }
@@ -256,16 +277,43 @@
 int
 Android_OnHat(int device_id, int hat_id, int x, int y)
 {
-    const Uint8 position_map[3][3] = {
-        {SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP},
-        {SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT},
-        {SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN}
-    };
+    const int DPAD_UP_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_UP);
+    const int DPAD_DOWN_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN);
+    const int DPAD_LEFT_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT);
+    const int DPAD_RIGHT_MASK = (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
 
-    if (x >= -1 && x <=1 && y >= -1 && y <= 1) {
+    if (x >= -1 && x <= 1 && y >= -1 && y <= 1) {
         SDL_joylist_item *item = JoystickByDeviceId(device_id);
         if (item && item->joystick) {
-            SDL_PrivateJoystickHat(item->joystick, hat_id, position_map[y+1][x+1]);
+            int dpad_state = 0;
+            int dpad_delta;
+            if (x < 0) {
+                dpad_state |= DPAD_LEFT_MASK;
+            } else if (x > 0) {
+                dpad_state |= DPAD_RIGHT_MASK;
+            }
+            if (y < 0) {
+                dpad_state |= DPAD_UP_MASK;
+            } else if (y > 0) {
+                dpad_state |= DPAD_DOWN_MASK;
+            }
+
+            dpad_delta = (dpad_state ^ item->dpad_state);
+            if (dpad_delta) {
+                if (dpad_delta & DPAD_UP_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (dpad_state & DPAD_UP_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                if (dpad_delta & DPAD_DOWN_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (dpad_state & DPAD_DOWN_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                if (dpad_delta & DPAD_LEFT_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (dpad_state & DPAD_LEFT_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                if (dpad_delta & DPAD_RIGHT_MASK) {
+                    SDL_PrivateJoystickButton(item->joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (dpad_state & DPAD_RIGHT_MASK) ? SDL_PRESSED : SDL_RELEASED);
+                }
+                item->dpad_state = dpad_state;
+            }
         }
         return 0;
     }
@@ -275,10 +323,14 @@
 
 
 int
-Android_AddJoystick(int device_id, const char *name, const char *desc, SDL_bool is_accelerometer, int nbuttons, int naxes, int nhats, int nballs)
+Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, SDL_bool is_accelerometer, int button_mask, int naxes, int nhats, int nballs)
 {
-    SDL_JoystickGUID guid;
     SDL_joylist_item *item;
+    SDL_JoystickGUID guid;
+    Uint16 *guid16 = (Uint16 *)guid.data;
+    int i;
+    int axis_mask;
+
 
     if (!SDL_GetHintBoolean(SDL_HINT_TV_REMOTE_AS_JOYSTICK, SDL_TRUE)) {
         /* Ignore devices that aren't actually controllers (e.g. remotes), they'll be handled as keyboard input */
@@ -290,10 +342,65 @@
     if (JoystickByDeviceId(device_id) != NULL || name == NULL) {
         return -1;
     }
-    
-    /* the GUID is just the first 16 chars of the name for now */
-    SDL_zero(guid);
-    SDL_memcpy(&guid, desc, SDL_min(sizeof(guid), SDL_strlen(desc)));
+
+#ifdef SDL_JOYSTICK_HIDAPI
+    if (HIDAPI_IsDevicePresent(vendor_id, product_id, 0)) {
+        /* The HIDAPI driver is taking care of this device */
+        return -1;
+    }
+#endif
+
+#ifdef DEBUG_JOYSTICK
+    SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats\n", name, desc, vendor_id, product_id, naxes, nhats);
+#endif
+
+    /* Add the available buttons and axes
+       The axis mask should probably come from Java where there is more information about the axes...
+     */
+    axis_mask = 0;
+    if (!is_accelerometer) {
+        if (naxes >= 2) {
+            axis_mask |= ((1 << SDL_CONTROLLER_AXIS_LEFTX) | (1 << SDL_CONTROLLER_AXIS_LEFTY));
+        }
+        if (naxes >= 4) {
+            axis_mask |= ((1 << SDL_CONTROLLER_AXIS_RIGHTX) | (1 << SDL_CONTROLLER_AXIS_RIGHTY));
+        }
+        if (naxes >= 6) {
+            axis_mask |= ((1 << SDL_CONTROLLER_AXIS_TRIGGERLEFT) | (1 << SDL_CONTROLLER_AXIS_TRIGGERRIGHT));
+        }
+    }
+
+    if (nhats > 0) {
+        /* Hat is translated into DPAD buttons */
+        button_mask |= ((1 << SDL_CONTROLLER_BUTTON_DPAD_UP) |
+                        (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) |
+                        (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT) |
+                        (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT));
+        nhats = 0;
+    }
+
+    SDL_memset(guid.data, 0, sizeof(guid.data));
+
+    /* We only need 16 bits for each of these; space them out to fill 128. */
+    /* Byteswap so devices get same GUID on little/big endian platforms. */
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
+    *guid16++ = 0;
+
+    if (vendor_id && product_id) {
+        *guid16++ = SDL_SwapLE16(vendor_id);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(product_id);
+        *guid16++ = 0;
+    } else {
+        Uint32 crc = crc32(desc, SDL_strlen(desc));
+        SDL_memcpy(guid16, desc, SDL_min(2*sizeof(*guid16), SDL_strlen(desc)));
+        guid16 += 2;
+        *(Uint32 *)guid16 = SDL_SwapLE32(crc);
+        guid16 += 2;
+    }
+
+    *guid16++ = SDL_SwapLE16(button_mask);
+    *guid16++ = SDL_SwapLE16(axis_mask);
 
     item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
     if (item == NULL) {
@@ -310,16 +417,19 @@
     }
     
     item->is_accelerometer = is_accelerometer;
-    if (nbuttons > -1) {
-        item->nbuttons = nbuttons;
-    }
-    else {
+    if (button_mask == 0xFFFFFFFF) {
         item->nbuttons = ANDROID_MAX_NBUTTONS;
+    } else {
+        for (i = 0; i < sizeof(button_mask)*8; ++i) {
+            if (button_mask & (1 << i)) {
+                item->nbuttons = i+1;
+            }
+        }
     }
     item->naxes = naxes;
     item->nhats = nhats;
     item->nballs = nballs;
-    item->device_instance = instance_counter++;
+    item->device_instance = SDL_GetNextJoystickInstanceID();
     if (SDL_joylist_tail == NULL) {
         SDL_joylist = SDL_joylist_tail = item;
     } else {
@@ -330,7 +440,7 @@
     /* Need to increment the joystick count before we post the event */
     ++numjoysticks;
 
-    SDL_PrivateJoystickAdded(numjoysticks - 1);
+    SDL_PrivateJoystickAdded(item->device_instance);
 
 #ifdef DEBUG_JOYSTICK
     SDL_Log("Added joystick %s with device_id %d", name, device_id);
@@ -387,104 +497,29 @@
 }
 
 
-static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
+static void ANDROID_JoystickDetect();
+
+static int
+ANDROID_JoystickInit(void)
 {
-    SDL_joylist_item *item;
-    
-    item = (SDL_joylist_item *)SDL_calloc(1, sizeof (SDL_joylist_item));
-    if (item == NULL) {
-        return SDL_FALSE;
-    }
-
-    *device_instance = item->device_instance = instance_counter++;
-    item->device_id = -1;
-    item->name = SDL_strdup(name);
-    item->guid = guid;
-    SDL_GetSteamControllerInputs(&item->nbuttons,
-                                 &item->naxes,
-                                 &item->nhats);
-    item->m_bSteamController = SDL_TRUE;
-
-    if (SDL_joylist_tail == NULL) {
-        SDL_joylist = SDL_joylist_tail = item;
-    } else {
-        SDL_joylist_tail->next = item;
-        SDL_joylist_tail = item;
-    }
-
-    /* Need to increment the joystick count before we post the event */
-    ++numjoysticks;
-
-    SDL_PrivateJoystickAdded(numjoysticks - 1);
-
-    return SDL_TRUE;
-}
-
-static void SteamControllerDisconnectedCallback(int device_instance)
-{
-    SDL_joylist_item *item = SDL_joylist;
-    SDL_joylist_item *prev = NULL;
-    
-    while (item != NULL) {
-        if (item->device_instance == device_instance) {
-            break;
-        }
-        prev = item;
-        item = item->next;
-    }
-    
-    if (item == NULL) {
-        return;
-    }
-
-    if (item->joystick) {
-        item->joystick->hwdata = NULL;
-    }
-        
-    if (prev != NULL) {
-        prev->next = item->next;
-    } else {
-        SDL_assert(SDL_joylist == item);
-        SDL_joylist = item->next;
-    }
-    if (item == SDL_joylist_tail) {
-        SDL_joylist_tail = prev;
-    }
-
-    /* Need to decrement the joystick count before we post the event */
-    --numjoysticks;
-
-    SDL_PrivateJoystickRemoved(item->device_instance);
-
-    SDL_free(item->name);
-    SDL_free(item);
-}
-
-int
-SDL_SYS_JoystickInit(void)
-{
-    SDL_SYS_JoystickDetect();
+    ANDROID_JoystickDetect();
     
     if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
         /* Default behavior, accelerometer as joystick */
-        Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, ANDROID_ACCELEROMETER_NAME, SDL_TRUE, 0, 3, 0, 0);
+        Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, ANDROID_ACCELEROMETER_NAME, 0, 0, SDL_TRUE, 0, 3, 0, 0);
     }
-   
-    SDL_InitSteamControllers(SteamControllerConnectedCallback,
-                             SteamControllerDisconnectedCallback);
-
-    return (numjoysticks);
+    return 0;
 
 }
 
-int
-SDL_SYS_NumJoysticks(void)
+static int
+ANDROID_JoystickGetCount(void)
 {
     return numjoysticks;
 }
 
-void
-SDL_SYS_JoystickDetect(void)
+static void
+ANDROID_JoystickDetect(void)
 {
     /* Support for device connect/disconnect is API >= 16 only,
      * so we poll every three seconds
@@ -495,8 +530,6 @@
         timeout = SDL_GetTicks() + 3000;
         Android_JNI_PollInputDevices();
     }
-
-    SDL_UpdateSteamControllers();
 }
 
 static SDL_joylist_item *
@@ -530,7 +563,7 @@
     }
     
     /* Joystick not found, try adding it */
-    SDL_SYS_JoystickDetect();
+    ANDROID_JoystickDetect();
     
     while (item != NULL) {
         if (item->device_id == device_id) {
@@ -542,26 +575,32 @@
     return NULL;
 }
 
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+ANDROID_JoystickGetDeviceName(int device_index)
 {
     return JoystickByDevIndex(device_index)->name;
 }
 
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static int
+ANDROID_JoystickGetDevicePlayerIndex(int device_index)
+{
+    return -1;
+}
+
+static SDL_JoystickGUID
+ANDROID_JoystickGetDeviceGUID(int device_index)
+{
+    return JoystickByDevIndex(device_index)->guid;
+}
+
+static SDL_JoystickID
+ANDROID_JoystickGetDeviceInstanceID(int device_index)
 {
     return JoystickByDevIndex(device_index)->device_instance;
 }
 
-/* Function to open a joystick for use.
-   The joystick to open is specified by the device index.
-   This should fill the nbuttons and naxes fields of the joystick structure.
-   It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+ANDROID_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     SDL_joylist_item *item = JoystickByDevIndex(device_index);
 
@@ -584,14 +623,14 @@
     return (0);
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static int
+ANDROID_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
 {
-    return joystick->hwdata != NULL;
+    return SDL_Unsupported();
 }
 
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static void
+ANDROID_JoystickUpdate(SDL_Joystick * joystick)
 {
     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
 
@@ -599,11 +638,6 @@
         return;
     }
  
-    if (item->m_bSteamController) {
-        SDL_UpdateSteamController(joystick);
-        return;
-    }
-
     if (item->is_accelerometer) {
         int i;
         Sint16 value;
@@ -624,9 +658,8 @@
     }
 }
 
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+ANDROID_JoystickClose(SDL_Joystick * joystick)
 {
     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
     if (item) {
@@ -634,9 +667,8 @@
     }
 }
 
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+ANDROID_JoystickQuit(void)
 {
 /* We don't have any way to scan for joysticks at init, so don't wipe the list
  * of joysticks here in case this is a reinit.
@@ -654,33 +686,24 @@
     SDL_joylist = SDL_joylist_tail = NULL;
 
     numjoysticks = 0;
-    instance_counter = 0;
 #endif /* 0 */
-
-    SDL_QuitSteamControllers();
 }
 
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
+SDL_JoystickDriver SDL_ANDROID_JoystickDriver =
 {
-    return JoystickByDevIndex(device_index)->guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
-    SDL_JoystickGUID guid;
-    
-    if (joystick->hwdata != NULL) {
-        return ((SDL_joylist_item*)joystick->hwdata)->guid;
-    }
-    
-    SDL_zero(guid);
-    return guid;
-}
-
-SDL_bool SDL_SYS_IsDPAD_DeviceIndex(int device_index)
-{
-    return JoystickByDevIndex(device_index)->naxes == 0;
-}
+    ANDROID_JoystickInit,
+    ANDROID_JoystickGetCount,
+    ANDROID_JoystickDetect,
+    ANDROID_JoystickGetDeviceName,
+    ANDROID_JoystickGetDevicePlayerIndex,
+    ANDROID_JoystickGetDeviceGUID,
+    ANDROID_JoystickGetDeviceInstanceID,
+    ANDROID_JoystickOpen,
+    ANDROID_JoystickRumble,
+    ANDROID_JoystickUpdate,
+    ANDROID_JoystickClose,
+    ANDROID_JoystickQuit,
+};
 
 #endif /* SDL_JOYSTICK_ANDROID */
 
diff --git a/source/src/joystick/android/SDL_sysjoystick_c.h b/source/src/joystick/android/SDL_sysjoystick_c.h
index c2cbc4e..20d7381 100644
--- a/source/src/joystick/android/SDL_sysjoystick_c.h
+++ b/source/src/joystick/android/SDL_sysjoystick_c.h
@@ -32,7 +32,7 @@
 extern int Android_OnPadUp(int device_id, int keycode);
 extern int Android_OnJoy(int device_id, int axisnum, float value);
 extern int Android_OnHat(int device_id, int hat_id, int x, int y);
-extern int Android_AddJoystick(int device_id, const char *name, const char *desc, SDL_bool is_accelerometer, int nbuttons, int naxes, int nhats, int nballs);
+extern int Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, SDL_bool is_accelerometer, int button_mask, int naxes, int nhats, int nballs);
 extern int Android_RemoveJoystick(int device_id);
 
 /* A linked list of available joysticks */
@@ -45,10 +45,8 @@
     SDL_bool is_accelerometer;
     SDL_Joystick *joystick;
     int nbuttons, naxes, nhats, nballs;
+    int dpad_state;
     
-    /* Steam Controller support */
-    SDL_bool m_bSteamController;
-
     struct SDL_joylist_item *next;
 } SDL_joylist_item;
 
diff --git a/source/src/joystick/bsd/SDL_sysjoystick.c b/source/src/joystick/bsd/SDL_sysjoystick.c
index 9408545..679b80c 100644
--- a/source/src/joystick/bsd/SDL_sysjoystick.c
+++ b/source/src/joystick/bsd/SDL_sysjoystick.c
@@ -161,15 +161,18 @@
 #define REP_BUF_DATA(rep) ((rep)->buf->data)
 #endif
 
-static int SDL_SYS_numjoysticks = 0;
+static int numjoysticks = 0;
 
-int
-SDL_SYS_JoystickInit(void)
+static int BSD_JoystickOpen(SDL_Joystick * joy, int device_index);
+static void BSD_JoystickClose(SDL_Joystick * joy);
+
+static int
+BSD_JoystickInit(void)
 {
     char s[16];
     int i, fd;
 
-    SDL_SYS_numjoysticks = 0;
+    numjoysticks = 0;
 
     SDL_memset(joynames, 0, sizeof(joynames));
     SDL_memset(joydevnames, 0, sizeof(joydevnames));
@@ -179,21 +182,21 @@
 
         SDL_snprintf(s, SDL_arraysize(s), "/dev/uhid%d", i);
 
-        joynames[SDL_SYS_numjoysticks] = SDL_strdup(s);
+        joynames[numjoysticks] = SDL_strdup(s);
 
-        if (SDL_SYS_JoystickOpen(&nj, SDL_SYS_numjoysticks) == 0) {
-            SDL_SYS_JoystickClose(&nj);
-            SDL_SYS_numjoysticks++;
+        if (BSD_JoystickOpen(&nj, numjoysticks) == 0) {
+            BSD_JoystickClose(&nj);
+            numjoysticks++;
         } else {
-            SDL_free(joynames[SDL_SYS_numjoysticks]);
-            joynames[SDL_SYS_numjoysticks] = NULL;
+            SDL_free(joynames[numjoysticks]);
+            joynames[numjoysticks] = NULL;
         }
     }
     for (i = 0; i < MAX_JOY_JOYS; i++) {
         SDL_snprintf(s, SDL_arraysize(s), "/dev/joy%d", i);
         fd = open(s, O_RDONLY);
         if (fd != -1) {
-            joynames[SDL_SYS_numjoysticks++] = SDL_strdup(s);
+            joynames[numjoysticks++] = SDL_strdup(s);
             close(fd);
         }
     }
@@ -201,22 +204,22 @@
     /* Read the default USB HID usage table. */
     hid_init(NULL);
 
-    return (SDL_SYS_numjoysticks);
+    return (numjoysticks);
 }
 
-int
-SDL_SYS_NumJoysticks(void)
+static int
+BSD_JoystickGetCount(void)
 {
-    return SDL_SYS_numjoysticks;
+    return numjoysticks;
 }
 
-void
-SDL_SYS_JoystickDetect(void)
+static void
+BSD_JoystickDetect(void)
 {
 }
 
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+BSD_JoystickGetDeviceName(int device_index)
 {
     if (joydevnames[device_index] != NULL) {
         return (joydevnames[device_index]);
@@ -224,8 +227,15 @@
     return (joynames[device_index]);
 }
 
+static int
+BSD_JoystickGetDevicePlayerIndex(int device_index)
+{
+    return -1;
+}
+
 /* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickID
+BSD_JoystickGetDeviceInstanceID(int device_index)
 {
     return device_index;
 }
@@ -281,8 +291,8 @@
 }
 
 
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joy, int device_index)
+static int
+BSD_JoystickOpen(SDL_Joystick * joy, int device_index)
 {
     char *path = joynames[device_index];
     struct joystick_hwdata *hw;
@@ -365,8 +375,8 @@
         str[i] = '\0';
         asprintf(&new_name, "%s @ %s", str, path);
         if (new_name != NULL) {
-            SDL_free(joydevnames[SDL_SYS_numjoysticks]);
-            joydevnames[SDL_SYS_numjoysticks] = new_name;
+            SDL_free(joydevnames[numjoysticks]);
+            joydevnames[numjoysticks] = new_name;
         }
     }
 desc_failed:
@@ -467,14 +477,8 @@
     return (-1);
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
-{
-    return SDL_TRUE;
-}
-
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joy)
+static void
+BSD_JoystickUpdate(SDL_Joystick * joy)
 {
     struct hid_item hitem;
     struct hid_data *hdata;
@@ -586,8 +590,8 @@
 }
 
 /* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joy)
+static void
+BSD_JoystickClose(SDL_Joystick * joy)
 {
     if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) {
         report_free(&joy->hwdata->inreport);
@@ -598,8 +602,8 @@
     SDL_free(joy->hwdata);
 }
 
-void
-SDL_SYS_JoystickQuit(void)
+static void
+BSD_JoystickQuit(void)
 {
     int i;
 
@@ -611,21 +615,12 @@
     return;
 }
 
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+static SDL_JoystickGUID
+BSD_JoystickGetDeviceGUID( int device_index )
 {
     SDL_JoystickGUID guid;
     /* the GUID is just the first 16 chars of the name for now */
-    const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
-    SDL_zero( guid );
-    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
-    return guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
-    const char *name = joystick->name;
+    const char *name = BSD_JoystickGetDeviceName( device_index );
     SDL_zero( guid );
     SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
     return guid;
@@ -686,6 +681,28 @@
     r->status = SREPORT_UNINIT;
 }
 
+static int
+BSD_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    return SDL_Unsupported();
+}
+
+SDL_JoystickDriver SDL_BSD_JoystickDriver =
+{
+    BSD_JoystickInit,
+    BSD_JoystickGetCount,
+    BSD_JoystickDetect,
+    BSD_JoystickGetDeviceName,
+    BSD_JoystickGetDevicePlayerIndex,
+    BSD_JoystickGetDeviceGUID,
+    BSD_JoystickGetDeviceInstanceID,
+    BSD_JoystickOpen,
+    BSD_JoystickRumble,
+    BSD_JoystickUpdate,
+    BSD_JoystickClose,
+    BSD_JoystickQuit,
+};
+
 #endif /* SDL_JOYSTICK_USBHID */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/controller_type.h b/source/src/joystick/controller_type.h
new file mode 100644
index 0000000..51ac20b
--- /dev/null
+++ b/source/src/joystick/controller_type.h
@@ -0,0 +1,427 @@
+/*
+  Copyright (C) Valve Corporation
+
+  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 CONTROLLER_TYPE_H
+#define CONTROLLER_TYPE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#ifndef __cplusplus
+#define inline SDL_INLINE
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Steam Controller models 
+// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN A DATABASE
+//-----------------------------------------------------------------------------
+typedef enum
+{
+	k_eControllerType_None = -1,
+	k_eControllerType_Unknown = 0,
+
+	// Steam Controllers
+	k_eControllerType_UnknownSteamController = 1,
+	k_eControllerType_SteamController = 2,
+	k_eControllerType_SteamControllerV2 = 3,
+
+	// Other Controllers
+	k_eControllerType_UnknownNonSteamController = 30,
+	k_eControllerType_XBox360Controller = 31,
+	k_eControllerType_XBoxOneController = 32,
+	k_eControllerType_PS3Controller = 33,
+	k_eControllerType_PS4Controller = 34,
+	k_eControllerType_WiiController = 35,
+	k_eControllerType_AppleController = 36,
+	k_eControllerType_AndroidController = 37,
+	k_eControllerType_SwitchProController = 38,
+	k_eControllerType_SwitchJoyConLeft = 39,
+	k_eControllerType_SwitchJoyConRight = 40,
+	k_eControllerType_SwitchJoyConPair = 41,
+    k_eControllerType_SwitchInputOnlyController = 42,
+	k_eControllerType_MobileTouch = 43,
+	k_eControllerType_LastController,			// Don't add game controllers below this enumeration - this enumeration can change value
+
+	// Keyboards and Mice
+	k_eControllertype_GenericKeyboard = 400,
+	k_eControllertype_GenericMouse = 800,
+} EControllerType;
+
+static inline SDL_bool BIsSteamController( EControllerType eType )
+{
+	return ( eType == k_eControllerType_SteamController || eType == k_eControllerType_SteamControllerV2 );
+}
+
+#define MAKE_CONTROLLER_ID( nVID, nPID )	(unsigned int)( nVID << 16 | nPID )
+typedef struct
+{
+	unsigned int m_unDeviceID;
+	EControllerType m_eControllerType;
+} ControllerDescription_t;
+
+static const ControllerDescription_t arrControllers[] = {
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x18d4 ), k_eControllerType_XBox360Controller },	// GPD Win 2 X-Box Controller
+	{ MAKE_CONTROLLER_ID( 0x044f, 0xb326 ), k_eControllerType_XBox360Controller },	// Thrustmaster Gamepad GP XID
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x028e ), k_eControllerType_XBox360Controller },	// Microsoft X-Box 360 pad
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x028f ), k_eControllerType_XBox360Controller },	// Microsoft X-Box 360 pad v2
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x0291 ), k_eControllerType_XBox360Controller },	// Xbox 360 Wireless Receiver (XBOX)
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02a0 ), k_eControllerType_XBox360Controller },	// Microsoft X-Box 360 Big Button IR
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02a1 ), k_eControllerType_XBox360Controller },	// Microsoft X-Box 360 pad
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02d1 ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One pad
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02dd ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One pad (Firmware 2015)
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02e0 ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One S pad (Bluetooth)
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02e3 ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One Elite pad
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02ea ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One S pad
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02fd ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One S pad (Bluetooth)
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02ff ), k_eControllerType_XBoxOneController },	// Microsoft X-Box One Elite pad
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x0719 ), k_eControllerType_XBox360Controller },	// Xbox 360 Wireless Receiver
+	{ MAKE_CONTROLLER_ID( 0x046d, 0xc21d ), k_eControllerType_XBox360Controller },	// Logitech Gamepad F310
+	{ MAKE_CONTROLLER_ID( 0x046d, 0xc21e ), k_eControllerType_XBox360Controller },	// Logitech Gamepad F510
+	{ MAKE_CONTROLLER_ID( 0x046d, 0xc21f ), k_eControllerType_XBox360Controller },	// Logitech Gamepad F710
+	{ MAKE_CONTROLLER_ID( 0x046d, 0xc242 ), k_eControllerType_XBox360Controller },	// Logitech Chillstream Controller
+
+	{ MAKE_CONTROLLER_ID( 0x054c, 0x0268 ), k_eControllerType_PS3Controller },		// Sony PS3 Controller
+	{ MAKE_CONTROLLER_ID( 0x0925, 0x0005 ), k_eControllerType_PS3Controller },		// Sony PS3 Controller
+	{ MAKE_CONTROLLER_ID( 0x8888, 0x0308 ), k_eControllerType_PS3Controller },		// Sony PS3 Controller
+	{ MAKE_CONTROLLER_ID( 0x1a34, 0x0836 ), k_eControllerType_PS3Controller },		// Afterglow ps3
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x006e ), k_eControllerType_PS3Controller },		// HORI horipad4 ps3
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0066 ), k_eControllerType_PS3Controller },		// HORI horipad4 ps4
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x005f ), k_eControllerType_PS3Controller },		// HORI Fighting commander ps3
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x005e ), k_eControllerType_PS3Controller },		// HORI Fighting commander ps4
+	//{ MAKE_CONTROLLER_ID( 0x0738, 0x3250 ), k_eControllerType_PS3Controller },		// madcats fightpad pro ps3 already in ps4 list.. does this work??
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x8250 ), k_eControllerType_PS3Controller },		// madcats fightpad pro ps4
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x181a ), k_eControllerType_PS3Controller },		// Venom Arcade Stick
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x0006 ), k_eControllerType_PS3Controller },		// PC Twin Shock Controller - looks like a DS3 but the face buttons are 1-4 instead of symbols
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x1844 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x8888, 0x0308 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x2563, 0x0575 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x0810, 0x0001 ), k_eControllerType_PS3Controller },		// actually ps2 - maybe break out later
+	{ MAKE_CONTROLLER_ID( 0x0810, 0x0003 ), k_eControllerType_PS3Controller },		// actually ps2 - maybe break out later
+	{ MAKE_CONTROLLER_ID( 0x2563, 0x0523 ), k_eControllerType_PS3Controller },		// Digiflip GP006
+	{ MAKE_CONTROLLER_ID( 0x11ff, 0x3331 ), k_eControllerType_PS3Controller },		// SRXJ-PH2400
+	{ MAKE_CONTROLLER_ID( 0x20bc, 0x5500 ), k_eControllerType_PS3Controller },		// ShanWan PS3
+	{ MAKE_CONTROLLER_ID( 0x05b8, 0x1004 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x0603 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x044f, 0xb315 ), k_eControllerType_PS3Controller },		// Firestorm Dual Analog 3
+	{ MAKE_CONTROLLER_ID( 0x0925, 0x8888 ), k_eControllerType_PS3Controller },		// Actually ps2 -maybe break out later Lakeview Research WiseGroup Ltd, MP-8866 Dual Joypad
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x004d ), k_eControllerType_PS3Controller },		// Horipad 3
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0009 ), k_eControllerType_PS3Controller },		// HORI BDA GP1
+	{ MAKE_CONTROLLER_ID( 0x0e8f, 0x0008 ), k_eControllerType_PS3Controller },		// Green Asia
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x006a ), k_eControllerType_PS3Controller },		// Real Arcade Pro 4
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x011e ), k_eControllerType_PS3Controller },		// Rock Candy PS4
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0214 ), k_eControllerType_PS3Controller },		// afterglow ps3
+	{ MAKE_CONTROLLER_ID( 0x0925, 0x8866 ), k_eControllerType_PS3Controller },		// PS2 maybe break out later
+	{ MAKE_CONTROLLER_ID( 0x0e8f, 0x310d ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x2c22, 0x2003 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x056e, 0x2013 ), k_eControllerType_PS3Controller },		// JC-U4113SBK
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x8838 ), k_eControllerType_PS3Controller },		// Madcatz Fightstick Pro
+	{ MAKE_CONTROLLER_ID( 0x1a34, 0x0836 ), k_eControllerType_PS3Controller },		// Afterglow PS3
+	{ MAKE_CONTROLLER_ID( 0x0f30, 0x1100 ), k_eControllerType_PS3Controller },		// Quanba Q1 fight stick
+	{ MAKE_CONTROLLER_ID( 0x1345, 0x6005 ), k_eControllerType_PS3Controller },		// ps2 maybe break out later
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0087 ), k_eControllerType_PS3Controller },		// HORI fighting mini stick
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x5500 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x20d6, 0xca6d ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x25f0, 0xc121 ), k_eControllerType_PS3Controller },		//
+	{ MAKE_CONTROLLER_ID( 0x8380, 0x0003 ), k_eControllerType_PS3Controller },		// BTP 2163
+	{ MAKE_CONTROLLER_ID( 0x1345, 0x1000 ), k_eControllerType_PS3Controller },		// PS2 ACME GA-D5
+	{ MAKE_CONTROLLER_ID( 0x0e8f, 0x3075 ), k_eControllerType_PS3Controller },		// SpeedLink Strike FX
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0128 ), k_eControllerType_PS3Controller },		// Rock Candy PS3
+	{ MAKE_CONTROLLER_ID( 0x2c22, 0x2000 ), k_eControllerType_PS3Controller },		// Quanba Drone
+	{ MAKE_CONTROLLER_ID( 0x06a3, 0xf622 ), k_eControllerType_PS3Controller },		// Cyborg V3
+	{ MAKE_CONTROLLER_ID( 0x044f, 0xd007 ), k_eControllerType_PS3Controller },		// Thrustmaster wireless 3-1
+	{ MAKE_CONTROLLER_ID( 0x25f0, 0x83c3 ), k_eControllerType_PS3Controller },		// gioteck vx2
+	{ MAKE_CONTROLLER_ID( 0x05b8, 0x1006 ), k_eControllerType_PS3Controller },		// JC-U3412SBK
+	{ MAKE_CONTROLLER_ID( 0x20d6, 0x576d ), k_eControllerType_PS3Controller },		// Power A PS3
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x6302 ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x056e, 0x200f ), k_eControllerType_PS3Controller },		// From SDL
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x1314 ), k_eControllerType_PS3Controller },		// PDP Afterglow Wireless PS3 controller
+
+	{ MAKE_CONTROLLER_ID( 0x054c, 0x05c4 ), k_eControllerType_PS4Controller },		// Sony PS4 Controller
+	{ MAKE_CONTROLLER_ID( 0x054c, 0x09cc ), k_eControllerType_PS4Controller },		// Sony PS4 Slim Controller
+	{ MAKE_CONTROLLER_ID( 0x054c, 0x0ba0 ), k_eControllerType_PS4Controller },		// Sony PS4 Controller (Wireless dongle)
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x008a ), k_eControllerType_PS4Controller },		// HORI Real Arcade Pro 4
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0055 ), k_eControllerType_PS4Controller },		// HORIPAD 4 FPS
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0066 ), k_eControllerType_PS4Controller },		// HORIPAD 4 FPS Plus 
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x8384 ), k_eControllerType_PS4Controller },		// Mad Catz FightStick TE S+ PS4
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x8250 ), k_eControllerType_PS4Controller },		// Mad Catz FightPad Pro PS4
+	{ MAKE_CONTROLLER_ID( 0x0C12, 0x0E10 ), k_eControllerType_PS4Controller },		// Armor Armor 3 Pad PS4
+	{ MAKE_CONTROLLER_ID( 0x0C12, 0x1CF6 ), k_eControllerType_PS4Controller },		// EMIO PS4 Elite Controller
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x1000 ), k_eControllerType_PS4Controller },		// Razer Raiju PS4 Controller
+	{ MAKE_CONTROLLER_ID( 0x1532, 0X0401 ), k_eControllerType_PS4Controller },		// Razer Panthera PS4 Controller
+	{ MAKE_CONTROLLER_ID( 0x054c, 0x05c5 ), k_eControllerType_PS4Controller },		// STRIKEPAD PS4 Grip Add-on
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x0d01 ), k_eControllerType_PS4Controller },		// Nacon Revolution Pro Controller - has gyro
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x0d02 ), k_eControllerType_PS4Controller },		// Nacon Revolution Pro Controller v2 - has gyro
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00a0 ), k_eControllerType_PS4Controller },		// HORI TAC4 mousething
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x009c ), k_eControllerType_PS4Controller },		// HORI TAC PRO mousething
+	{ MAKE_CONTROLLER_ID( 0x0c12, 0x0ef6 ), k_eControllerType_PS4Controller },		// Hitbox Arcade Stick
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x181b ), k_eControllerType_PS4Controller },		// Venom Arcade Stick - XXX:this may not work and may need to be called a ps3 controller
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x3250 ), k_eControllerType_PS4Controller },		// Mad Catz FightPad PRO - controller shaped with 6 face buttons
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00ee ), k_eControllerType_PS4Controller },		// Hori mini wired https://www.playstation.com/en-us/explore/accessories/gaming-controllers/mini-wired-gamepad/
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x8481 ), k_eControllerType_PS4Controller },		// Mad Catz FightStick TE 2+ PS4
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x8480 ), k_eControllerType_PS4Controller },		// Mad Catz FightStick TE 2 PS4
+	{ MAKE_CONTROLLER_ID( 0x7545, 0x0104 ), k_eControllerType_PS4Controller },		// Armor 3 or Level Up Cobra - At least one variant has gyro
+	{ MAKE_CONTROLLER_ID( 0x0c12, 0x0e15 ), k_eControllerType_PS4Controller },		// Game:Pad 4
+	{ MAKE_CONTROLLER_ID( 0x11c0, 0x4001 ), k_eControllerType_PS4Controller },		// "PS4 Fun Controller" added from user log
+
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x1007 ), k_eControllerType_PS4Controller },		// Razer Raiju 2 Tournament edition USB- untested and added for razer
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x100A ), k_eControllerType_PS4Controller },		// Razer Raiju 2 Tournament edition BT - untested and added for razer
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x1004 ), k_eControllerType_PS4Controller },		// Razer Raiju 2 Ultimate USB - untested and added for razer
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x1009 ), k_eControllerType_PS4Controller },		// Razer Raiju 2 Ultimate BT - untested and added for razer
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x1008 ), k_eControllerType_PS4Controller },		// Razer Panthera Evo Fightstick - untested and added for razer
+
+	{ MAKE_CONTROLLER_ID( 0x056e, 0x2004 ), k_eControllerType_XBox360Controller },	// Elecom JC-U3613M
+	{ MAKE_CONTROLLER_ID( 0x06a3, 0xf51a ), k_eControllerType_XBox360Controller },	// Saitek P3600
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4716 ), k_eControllerType_XBox360Controller },	// Mad Catz Wired Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4718 ), k_eControllerType_XBox360Controller },	// Mad Catz Street Fighter IV FightStick SE
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4726 ), k_eControllerType_XBox360Controller },	// Mad Catz Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4728 ), k_eControllerType_XBox360Controller },	// Mad Catz Street Fighter IV FightPad
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4736 ), k_eControllerType_XBox360Controller },	// Mad Catz MicroCon Gamepad
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4738 ), k_eControllerType_XBox360Controller },	// Mad Catz Wired Xbox 360 Controller (SFIV)
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4740 ), k_eControllerType_XBox360Controller },	// Mad Catz Beat Pad
+	{ MAKE_CONTROLLER_ID( 0x0738, 0x4a01 ), k_eControllerType_XBoxOneController },	// Mad Catz FightStick TE 2
+	{ MAKE_CONTROLLER_ID( 0x0738, 0xb726 ), k_eControllerType_XBox360Controller },	// Mad Catz Xbox controller - MW2
+	{ MAKE_CONTROLLER_ID( 0x0738, 0xbeef ), k_eControllerType_XBox360Controller },	// Mad Catz JOYTECH NEO SE Advanced GamePad
+	{ MAKE_CONTROLLER_ID( 0x0738, 0xcb02 ), k_eControllerType_XBox360Controller },	// Saitek Cyborg Rumble Pad - PC/Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x0738, 0xcb03 ), k_eControllerType_XBox360Controller },	// Saitek P3200 Rumble Pad - PC/Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x0738, 0xf738 ), k_eControllerType_XBox360Controller },	// Super SFIV FightStick TE S
+	{ MAKE_CONTROLLER_ID( 0x0955, 0xb400 ), k_eControllerType_XBox360Controller },	// NVIDIA Shield streaming controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0105 ), k_eControllerType_XBox360Controller },	// HSM3 Xbox360 dancepad
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0113 ), k_eControllerType_XBox360Controller },	// Afterglow AX.1 Gamepad for Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x011f ), k_eControllerType_XBox360Controller },	// Rock Candy Gamepad Wired Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0131 ), k_eControllerType_XBox360Controller },	// PDP EA Sports Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0133 ), k_eControllerType_XBox360Controller },	// Xbox 360 Wired Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0139 ), k_eControllerType_XBoxOneController },	// Afterglow Prismatic Wired Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x013a ), k_eControllerType_XBoxOneController },	// PDP Xbox One Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0146 ), k_eControllerType_XBoxOneController },	// Rock Candy Wired Controller for Xbox One
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0147 ), k_eControllerType_XBoxOneController },	// PDP Marvel Xbox One Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x015c ), k_eControllerType_XBoxOneController },	// PDP Xbox One Arcade Stick
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0161 ), k_eControllerType_XBoxOneController },	// PDP Xbox One Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0162 ), k_eControllerType_XBoxOneController },	// PDP Xbox One Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0163 ), k_eControllerType_XBoxOneController },	// PDP Xbox One Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0164 ), k_eControllerType_XBoxOneController },	// PDP Battlefield One
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0165 ), k_eControllerType_XBoxOneController },	// PDP Titanfall 2
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0201 ), k_eControllerType_XBox360Controller },	// Pelican PL-3601 'TSZ' Wired Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0213 ), k_eControllerType_XBox360Controller },	// Afterglow Gamepad for Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x021f ), k_eControllerType_XBox360Controller },	// Rock Candy Gamepad for Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0246 ), k_eControllerType_XBoxOneController },	// Rock Candy Gamepad for Xbox One 2015
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x02a0 ), k_eControllerType_XBox360Controller },	// Counterfeit 360Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0301 ), k_eControllerType_XBox360Controller },	// Logic3 Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0346 ), k_eControllerType_XBoxOneController },	// Rock Candy Gamepad for Xbox One 2016
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0401 ), k_eControllerType_XBox360Controller },	// Logic3 Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0413 ), k_eControllerType_XBox360Controller },	// Afterglow AX.1 Gamepad for Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0501 ), k_eControllerType_XBox360Controller },	// PDP Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0xf501 ), k_eControllerType_XBox360Controller },	// Counterfeit 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0xf900 ), k_eControllerType_XBox360Controller },	// PDP Afterglow AX.1
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x000a ), k_eControllerType_XBox360Controller },	// Hori Co. DOA4 FightStick
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x000c ), k_eControllerType_XBox360Controller },	// Hori PadEX Turbo
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x000d ), k_eControllerType_XBox360Controller },	// Hori Fighting Stick EX2
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0016 ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro.EX
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x001b ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro VX
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0063 ), k_eControllerType_XBoxOneController },	// Hori Real Arcade Pro Hayabusa (USA) Xbox One
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0067 ), k_eControllerType_XBoxOneController },	// HORIPAD ONE
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0078 ), k_eControllerType_XBoxOneController },	// Hori Real Arcade Pro V Kai Xbox One
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x008c ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro 4
+	{ MAKE_CONTROLLER_ID( 0x11c9, 0x55f0 ), k_eControllerType_XBox360Controller },	// Nacon GC-100XF
+	{ MAKE_CONTROLLER_ID( 0x12ab, 0x0004 ), k_eControllerType_XBox360Controller },	// Honey Bee Xbox360 dancepad
+	{ MAKE_CONTROLLER_ID( 0x12ab, 0x0301 ), k_eControllerType_XBox360Controller },	// PDP AFTERGLOW AX.1
+	{ MAKE_CONTROLLER_ID( 0x12ab, 0x0303 ), k_eControllerType_XBox360Controller },	// Mortal Kombat Klassic FightStick
+	{ MAKE_CONTROLLER_ID( 0x1430, 0x02a0 ), k_eControllerType_XBox360Controller },	// RedOctane Controller Adapter
+	{ MAKE_CONTROLLER_ID( 0x1430, 0x4748 ), k_eControllerType_XBox360Controller },	// RedOctane Guitar Hero X-plorer
+	{ MAKE_CONTROLLER_ID( 0x1430, 0xf801 ), k_eControllerType_XBox360Controller },	// RedOctane Controller
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x0601 ), k_eControllerType_XBox360Controller },	// BigBen Interactive XBOX 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x0037 ), k_eControllerType_XBox360Controller },	// Razer Sabertooth
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x0a00 ), k_eControllerType_XBoxOneController },	// Razer Atrox Arcade Stick
+	{ MAKE_CONTROLLER_ID( 0x1532, 0x0a03 ), k_eControllerType_XBoxOneController },	// Razer Wildcat
+	{ MAKE_CONTROLLER_ID( 0x15e4, 0x3f00 ), k_eControllerType_XBox360Controller },	// Power A Mini Pro Elite
+	{ MAKE_CONTROLLER_ID( 0x15e4, 0x3f0a ), k_eControllerType_XBox360Controller },	// Xbox Airflo wired controller
+	{ MAKE_CONTROLLER_ID( 0x15e4, 0x3f10 ), k_eControllerType_XBox360Controller },	// Batarang Xbox 360 controller
+	{ MAKE_CONTROLLER_ID( 0x162e, 0xbeef ), k_eControllerType_XBox360Controller },	// Joytech Neo-Se Take2
+	{ MAKE_CONTROLLER_ID( 0x1689, 0xfd00 ), k_eControllerType_XBox360Controller },	// Razer Onza Tournament Edition
+	{ MAKE_CONTROLLER_ID( 0x1689, 0xfd01 ), k_eControllerType_XBox360Controller },	// Razer Onza Classic Edition
+	{ MAKE_CONTROLLER_ID( 0x1689, 0xfe00 ), k_eControllerType_XBox360Controller },	// Razer Sabertooth
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0x0002 ), k_eControllerType_XBox360Controller },	// Harmonix Rock Band Guitar
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0x0003 ), k_eControllerType_XBox360Controller },	// Harmonix Rock Band Drumkit
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf016 ), k_eControllerType_XBox360Controller },	// Mad Catz Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf018 ), k_eControllerType_XBox360Controller },	// Mad Catz Street Fighter IV SE Fighting Stick
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf019 ), k_eControllerType_XBox360Controller },	// Mad Catz Brawlstick for Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf021 ), k_eControllerType_XBox360Controller },	// Mad Cats Ghost Recon FS GamePad
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf023 ), k_eControllerType_XBox360Controller },	// MLG Pro Circuit Controller (Xbox)
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf025 ), k_eControllerType_XBox360Controller },	// Mad Catz Call Of Duty
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf027 ), k_eControllerType_XBox360Controller },	// Mad Catz FPS Pro
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf028 ), k_eControllerType_XBox360Controller },	// Street Fighter IV FightPad
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf02e ), k_eControllerType_XBox360Controller },	// Mad Catz Fightpad
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf036 ), k_eControllerType_XBox360Controller },	// Mad Catz MicroCon GamePad Pro
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf038 ), k_eControllerType_XBox360Controller },	// Street Fighter IV FightStick TE
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf039 ), k_eControllerType_XBox360Controller },	// Mad Catz MvC2 TE
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf03a ), k_eControllerType_XBox360Controller },	// Mad Catz SFxT Fightstick Pro
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf03d ), k_eControllerType_XBox360Controller },	// Street Fighter IV Arcade Stick TE - Chun Li
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf03e ), k_eControllerType_XBox360Controller },	// Mad Catz MLG FightStick TE
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf03f ), k_eControllerType_XBox360Controller },	// Mad Catz FightStick SoulCaliber
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf042 ), k_eControllerType_XBox360Controller },	// Mad Catz FightStick TES+
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf080 ), k_eControllerType_XBox360Controller },	// Mad Catz FightStick TE2
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf501 ), k_eControllerType_XBox360Controller },	// HoriPad EX2 Turbo
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf502 ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro.VX SA
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf503 ), k_eControllerType_XBox360Controller },	// Hori Fighting Stick VX
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf504 ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro. EX
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf505 ), k_eControllerType_XBox360Controller },	// Hori Fighting Stick EX2B
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf506 ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro.EX Premium VLX
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf900 ), k_eControllerType_XBox360Controller },	// Harmonix Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf901 ), k_eControllerType_XBox360Controller },	// Gamestop Xbox 360 Controller
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf902 ), k_eControllerType_XBox360Controller },	// Mad Catz Gamepad2
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf903 ), k_eControllerType_XBox360Controller },	// Tron Xbox 360 controller
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf904 ), k_eControllerType_XBox360Controller },	// PDP Versus Fighting Pad
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xf906 ), k_eControllerType_XBox360Controller },	// MortalKombat FightStick
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xfa01 ), k_eControllerType_XBox360Controller },	// MadCatz GamePad
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xfd00 ), k_eControllerType_XBox360Controller },	// Razer Onza TE
+	{ MAKE_CONTROLLER_ID( 0x1bad, 0xfd01 ), k_eControllerType_XBox360Controller },	// Razer Onza
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5000 ), k_eControllerType_XBox360Controller },	// Razer Atrox Arcade Stick
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5300 ), k_eControllerType_XBox360Controller },	// PowerA MINI PROEX Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5303 ), k_eControllerType_XBox360Controller },	// Xbox Airflo wired controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x530a ), k_eControllerType_XBox360Controller },	// Xbox 360 Pro EX Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x531a ), k_eControllerType_XBox360Controller },	// PowerA Pro Ex
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5397 ), k_eControllerType_XBox360Controller },	// FUS1ON Tournament Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x541a ), k_eControllerType_XBoxOneController },	// PowerA Xbox One Mini Wired Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x542a ), k_eControllerType_XBoxOneController },	// Xbox ONE spectra
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x543a ), k_eControllerType_XBoxOneController },	// PowerA Xbox One wired controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5500 ), k_eControllerType_XBox360Controller },	// Hori XBOX 360 EX 2 with Turbo
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5501 ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro VX-SA
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5502 ), k_eControllerType_XBox360Controller },	// Hori Fighting Stick VX Alt
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5503 ), k_eControllerType_XBox360Controller },	// Hori Fighting Edge
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5506 ), k_eControllerType_XBox360Controller },	// Hori SOULCALIBUR V Stick
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5510 ), k_eControllerType_XBox360Controller },	// Hori Fighting Commander ONE
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x550d ), k_eControllerType_XBox360Controller },	// Hori GEM Xbox controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x550e ), k_eControllerType_XBox360Controller },	// Hori Real Arcade Pro V Kai 360
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x551a ), k_eControllerType_XBoxOneController },	// PowerA FUSION Pro Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x561a ), k_eControllerType_XBoxOneController },	// PowerA FUSION Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5b00 ), k_eControllerType_XBox360Controller },	// ThrustMaster Ferrari Italia 458 Racing Wheel
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5b02 ), k_eControllerType_XBox360Controller },	// Thrustmaster, Inc. GPX Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5b03 ), k_eControllerType_XBox360Controller },	// Thrustmaster Ferrari 458 Racing Wheel
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0x5d04 ), k_eControllerType_XBox360Controller },	// Razer Sabertooth
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafa ), k_eControllerType_XBox360Controller },	// Aplay Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafb ), k_eControllerType_XBox360Controller },	// Aplay Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafc ), k_eControllerType_XBox360Controller },	// Afterglow Gamepad 1
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafe ), k_eControllerType_XBox360Controller },	// Rock Candy Gamepad for Xbox 360
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafd ), k_eControllerType_XBox360Controller },	// Afterglow Gamepad 3
+	
+	// These have been added via Minidump for unrecognized Xinput controller assert
+	{ MAKE_CONTROLLER_ID( 0x0000, 0x0000 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x02a2 ), k_eControllerType_XBox360Controller },	// Unknown Controller - Microsoft VID
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x1414 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x1314 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0159 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x24c6, 0xfaff ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0086 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x006d ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00a4 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x1832 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x187f ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x1883 ), k_eControllerType_XBox360Controller },	// Unknown Controller	
+	{ MAKE_CONTROLLER_ID( 0x03eb, 0xff01 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x2c22, 0x2303 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0c12, 0x0ef8 ), k_eControllerType_XBox360Controller },	// Homemade fightstick based on brook pcb (with XInput driver??)
+	{ MAKE_CONTROLLER_ID( 0x046d, 0x1000 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x1345, 0x6006 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+
+	{ MAKE_CONTROLLER_ID( 0x056e, 0x2012 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x0602 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00ae ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x146b, 0x0603 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x056e, 0x2013 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x046d, 0x0401 ), k_eControllerType_XBox360Controller },	// logitech xinput
+	{ MAKE_CONTROLLER_ID( 0x046d, 0x0301 ), k_eControllerType_XBox360Controller },	// logitech xinput
+	{ MAKE_CONTROLLER_ID( 0x046d, 0xcaa3 ), k_eControllerType_XBox360Controller },	// logitech xinput
+	{ MAKE_CONTROLLER_ID( 0x046d, 0xc261 ), k_eControllerType_XBox360Controller },	// logitech xinput
+	{ MAKE_CONTROLLER_ID( 0x046d, 0x0291 ), k_eControllerType_XBox360Controller },	// logitech xinput
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x18d3 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00b1 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0001, 0x0001 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x1345, 0x6005 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x188e ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x18d4 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x2c22, 0x2003 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00b1 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x187c ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x189c ), k_eControllerType_XBox360Controller },	// Unknown Controller
+	{ MAKE_CONTROLLER_ID( 0x0079, 0x1874 ), k_eControllerType_XBox360Controller },	// Unknown Controller
+
+	{ MAKE_CONTROLLER_ID( 0x1038, 0xb360 ), k_eControllerType_XBox360Controller },	// SteelSeries Nimbus/Stratus XL
+
+																					
+	//{ MAKE_CONTROLLER_ID( 0x1949, 0x0402 ), /*android*/ },	// Unknown Controller
+
+	{ MAKE_CONTROLLER_ID( 0x05ac, 0x0001 ), k_eControllerType_AppleController },	// MFI Extended Gamepad (generic entry for iOS/tvOS)
+	{ MAKE_CONTROLLER_ID( 0x05ac, 0x0002 ), k_eControllerType_AppleController },	// MFI Standard Gamepad (generic entry for iOS/tvOS)
+
+    // We currently don't support using a pair of Switch Joy-Con's as a single
+    // controller and we don't want to support using them individually for the
+    // time being, so these should be disabled until one of the above is true
+    // { MAKE_CONTROLLER_ID( 0x057e, 0x2006 ), k_eControllerType_SwitchJoyConLeft },    // Nintendo Switch Joy-Con (Left)
+    // { MAKE_CONTROLLER_ID( 0x057e, 0x2007 ), k_eControllerType_SwitchJoyConRight },   // Nintendo Switch Joy-Con (Right)
+
+    // This same controller ID is spoofed by many 3rd-party Switch controllers.
+    // The ones we currently know of are:
+    // * Any 8bitdo controller with Switch support
+    // * ORTZ Gaming Wireless Pro Controller
+    // * ZhiXu Gamepad Wireless
+    // * Sunwaytek Wireless Motion Controller for Nintendo Switch
+	{ MAKE_CONTROLLER_ID( 0x057e, 0x2009 ), k_eControllerType_SwitchProController },        // Nintendo Switch Pro Controller
+    
+    { MAKE_CONTROLLER_ID( 0x0f0d, 0x00c1 ), k_eControllerType_SwitchInputOnlyController },  // HORIPAD for Nintendo Switch
+    { MAKE_CONTROLLER_ID( 0x20d6, 0xa711 ), k_eControllerType_SwitchInputOnlyController },  // PowerA Wired Controller Plus
+    { MAKE_CONTROLLER_ID( 0x0f0d, 0x0092 ), k_eControllerType_SwitchInputOnlyController },  // HORI Pokken Tournament DX Pro Pad
+
+
+	// Valve products - don't add to public list
+    { MAKE_CONTROLLER_ID( 0x0000, 0x11fb ), k_eControllerType_MobileTouch },		// Streaming mobile touch virtual controls
+	{ MAKE_CONTROLLER_ID( 0x28de, 0x1101 ), k_eControllerType_SteamController },	// Valve Legacy Steam Controller (CHELL)
+	{ MAKE_CONTROLLER_ID( 0x28de, 0x1102 ), k_eControllerType_SteamController },	// Valve wired Steam Controller (D0G)
+	{ MAKE_CONTROLLER_ID( 0x28de, 0x1105 ), k_eControllerType_SteamControllerV2 },	// Valve Bluetooth Steam Controller (D0G)
+	{ MAKE_CONTROLLER_ID( 0x28de, 0x1106 ), k_eControllerType_SteamControllerV2 },	// Valve Bluetooth Steam Controller (D0G)
+	{ MAKE_CONTROLLER_ID( 0x28de, 0x1142 ), k_eControllerType_SteamController },	// Valve wireless Steam Controller
+	{ MAKE_CONTROLLER_ID( 0x28de, 0x1201 ), k_eControllerType_SteamController },	// Valve wired Steam Controller (HEADCRAB)
+};
+
+
+#if 0  /* these are currently unused, so #if 0'd out to prevent compiler warnings for now */
+static inline const ControllerDescription_t * GetControllerArray( int* nLength /* Out */)
+{
+	*nLength = sizeof( arrControllers ) / sizeof( arrControllers[0] );
+	return arrControllers;
+}
+#endif
+
+static inline EControllerType GuessControllerType( int nVID, int nPID )
+{
+	unsigned int unDeviceID = MAKE_CONTROLLER_ID( nVID, nPID );
+	int iIndex;
+	for ( iIndex = 0; iIndex < sizeof( arrControllers ) / sizeof( arrControllers[0] ); ++iIndex )
+	{
+		if ( unDeviceID == arrControllers[ iIndex ].m_unDeviceID )
+		{
+			return arrControllers[ iIndex ].m_eControllerType;
+		}
+	}
+#undef MAKE_CONTROLLER_ID
+
+	return k_eControllerType_UnknownNonSteamController;
+}
+
+#endif // CONSTANTS_H
+
diff --git a/source/src/joystick/darwin/SDL_sysjoystick.c b/source/src/joystick/darwin/SDL_sysjoystick.c
index abfb1c6..8af3b96 100644
--- a/source/src/joystick/darwin/SDL_sysjoystick.c
+++ b/source/src/joystick/darwin/SDL_sysjoystick.c
@@ -22,20 +22,18 @@
 
 #ifdef SDL_JOYSTICK_IOKIT
 
-#include <IOKit/hid/IOHIDLib.h>
-
-/* For force feedback testing. */
-#include <ForceFeedback/ForceFeedback.h>
-#include <ForceFeedback/ForceFeedbackConstants.h>
-
+#include "SDL_events.h"
 #include "SDL_joystick.h"
 #include "../SDL_sysjoystick.h"
 #include "../SDL_joystick_c.h"
 #include "SDL_sysjoystick_c.h"
-#include "SDL_events.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
 #include "../../haptic/darwin/SDL_syshaptic_c.h"    /* For haptic hot plugging */
 
+
 #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
+
+#define CONVERT_MAGNITUDE(x)    (((x)*10000) / 0x7FFF)
 
 /* The base object of the HID Manager API */
 static IOHIDManagerRef hidman = NULL;
@@ -43,8 +41,60 @@
 /* Linked list of all available devices */
 static recDevice *gpDeviceList = NULL;
 
-/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
-static int s_joystick_instance_id = -1;
+void FreeRumbleEffectData(FFEFFECT *effect)
+{
+    if (!effect) {
+        return;
+    }
+    SDL_free(effect->rgdwAxes);
+    SDL_free(effect->rglDirection);
+    SDL_free(effect->lpvTypeSpecificParams);
+    SDL_free(effect);
+}
+
+FFEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
+{
+    FFEFFECT *effect;
+    FFPERIODIC *periodic;
+
+    /* Create the effect */
+    effect = (FFEFFECT *)SDL_calloc(1, sizeof(*effect));
+    if (!effect) {
+        return NULL;
+    }
+    effect->dwSize = sizeof(*effect);
+    effect->dwGain = 10000;
+    effect->dwFlags = FFEFF_OBJECTOFFSETS;
+    effect->dwDuration = duration_ms * 1000; /* In microseconds. */
+    effect->dwTriggerButton = FFEB_NOTRIGGER;
+
+    effect->cAxes = 2;
+    effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
+    if (!effect->rgdwAxes) {
+        FreeRumbleEffectData(effect);
+        return NULL;
+    }
+
+    effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
+    if (!effect->rglDirection) {
+        FreeRumbleEffectData(effect);
+        return NULL;
+    }
+    effect->dwFlags |= FFEFF_CARTESIAN;
+
+    periodic = (FFPERIODIC *)SDL_calloc(1, sizeof(*periodic));
+    if (!periodic) {
+        FreeRumbleEffectData(effect);
+        return NULL;
+    }
+    periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+    periodic->dwPeriod = 1000000;
+
+    effect->cbTypeSpecificParams = sizeof(*periodic);
+    effect->lpvTypeSpecificParams = periodic;
+
+    return effect;
+}
 
 static recDevice *GetDeviceForIndex(int device_index)
 {
@@ -157,6 +207,19 @@
     recDevice *device = (recDevice *) ctx;
     device->removed = SDL_TRUE;
     device->deviceRef = NULL; // deviceRef was invalidated due to the remove
+    if (device->ffeffect_ref) {
+        FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
+        device->ffeffect_ref = NULL;
+    }
+    if (device->ffeffect) {
+        FreeRumbleEffectData(device->ffeffect);
+        device->ffeffect = NULL;
+    }
+    if (device->ffdevice) {
+        FFReleaseDevice(device->ffdevice);
+        device->ffdevice = NULL;
+        device->ff_initialized = SDL_FALSE;
+    }
 #if SDL_HAPTIC_IOKIT
     MacHaptic_MaybeRemoveDevice(device->ffservice);
 #endif
@@ -333,8 +396,6 @@
 static SDL_bool
 GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
 {
-    const Uint16 BUS_USB = 0x03;
-    const Uint16 BUS_BLUETOOTH = 0x05;
     Sint32 vendor = 0;
     Sint32 product = 0;
     Sint32 version = 0;
@@ -389,10 +450,17 @@
         CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
     }
 
+#ifdef SDL_JOYSTICK_HIDAPI
+    if (HIDAPI_IsDevicePresent(vendor, product, version)) {
+        /* The HIDAPI driver is taking care of this device */
+        return 0;
+    }
+#endif
+
     SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
 
     if (vendor && product) {
-        *guid16++ = SDL_SwapLE16(BUS_USB);
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
         *guid16++ = 0;
         *guid16++ = SDL_SwapLE16((Uint16)vendor);
         *guid16++ = 0;
@@ -401,7 +469,7 @@
         *guid16++ = SDL_SwapLE16((Uint16)version);
         *guid16++ = 0;
     } else {
-        *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
         *guid16++ = 0;
         SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
     }
@@ -444,7 +512,6 @@
     }
 
     device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
-
     if (!device) {
         SDL_OutOfMemory();
         return;
@@ -455,8 +522,7 @@
         return;   /* not a device we care about, probably. */
     }
 
-    if (SDL_IsGameControllerNameAndGUID(device->product, device->guid) &&
-        SDL_ShouldIgnoreGameController(device->product, device->guid)) {
+    if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
         SDL_free(device);
         return;
     }
@@ -466,16 +532,16 @@
     IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
 
     /* Allocate an instance ID for this device */
-    device->instance_id = ++s_joystick_instance_id;
+    device->instance_id = SDL_GetNextJoystickInstanceID();
 
     /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
     ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
-#if SDL_HAPTIC_IOKIT
     if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
         device->ffservice = ioservice;
+#if SDL_HAPTIC_IOKIT
         MacHaptic_MaybeAddDevice(ioservice);
-    }
 #endif
+    }
 
     /* Add device to the end of the list */
     if ( !gpDeviceList ) {
@@ -492,7 +558,7 @@
         ++device_index;  /* bump by one since we counted by pNext. */
     }
 
-    SDL_PrivateJoystickAdded(device_index);
+    SDL_PrivateJoystickAdded(device->instance_id);
 }
 
 static SDL_bool
@@ -577,13 +643,8 @@
 }
 
 
-/* Function to scan the system for joysticks.
- * Joystick 0 should be the system default joystick.
- * This function should return the number of available joysticks, or -1
- * on an unrecoverable fatal error.
- */
-int
-SDL_SYS_JoystickInit(void)
+static int
+DARWIN_JoystickInit(void)
 {
     if (gpDeviceList) {
         return SDL_SetError("Joystick: Device list already inited.");
@@ -593,12 +654,11 @@
         return SDL_SetError("Joystick: Couldn't initialize HID Manager");
     }
 
-    return SDL_SYS_NumJoysticks();
+    return 0;
 }
 
-/* Function to return the number of joystick devices plugged in right now */
-int
-SDL_SYS_NumJoysticks(void)
+static int
+DARWIN_JoystickGetCount(void)
 {
     recDevice *device = gpDeviceList;
     int nJoySticks = 0;
@@ -613,10 +673,8 @@
     return nJoySticks;
 }
 
-/* Function to cause any queued joystick insertions to be processed
- */
-void
-SDL_SYS_JoystickDetect(void)
+static void
+DARWIN_JoystickDetect(void)
 {
     recDevice *device = gpDeviceList;
     while (device) {
@@ -627,37 +685,49 @@
         }
     }
 
-	/* run this after the checks above so we don't set device->removed and delete the device before
-	   SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
-	while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
-		/* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
-	}
+    /* run this after the checks above so we don't set device->removed and delete the device before
+       DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
+    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
+        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
+    }
 }
 
 /* Function to get the device-dependent name of a joystick */
 const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+DARWIN_JoystickGetDeviceName(int device_index)
 {
     recDevice *device = GetDeviceForIndex(device_index);
     return device ? device->product : "UNKNOWN";
 }
 
-/* Function to return the instance id of the joystick at device_index
- */
-SDL_JoystickID
-SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static int
+DARWIN_JoystickGetDevicePlayerIndex(int device_index)
+{
+    return -1;
+}
+
+static SDL_JoystickGUID
+DARWIN_JoystickGetDeviceGUID( int device_index )
+{
+    recDevice *device = GetDeviceForIndex(device_index);
+    SDL_JoystickGUID guid;
+    if (device) {
+        guid = device->guid;
+    } else {
+        SDL_zero(guid);
+    }
+    return guid;
+}
+
+static SDL_JoystickID
+DARWIN_JoystickGetDeviceInstanceID(int device_index)
 {
     recDevice *device = GetDeviceForIndex(device_index);
     return device ? device->instance_id : 0;
 }
 
-/* Function to open a joystick for use.
- * The joystick to open is specified by the device index.
- * This should fill the nbuttons and naxes fields of the joystick structure.
- * It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     recDevice *device = GetDeviceForIndex(device_index);
 
@@ -672,22 +742,138 @@
     return 0;
 }
 
-/* Function to query if the joystick is currently attached
- * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
+/*
+ * Like strerror but for force feedback errors.
  */
-SDL_bool
-SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
+static const char *
+FFStrError(unsigned int err)
 {
-    return joystick->hwdata != NULL;
+    switch (err) {
+    case FFERR_DEVICEFULL:
+        return "device full";
+    /* This should be valid, but for some reason isn't defined... */
+    /* case FFERR_DEVICENOTREG:
+        return "device not registered"; */
+    case FFERR_DEVICEPAUSED:
+        return "device paused";
+    case FFERR_DEVICERELEASED:
+        return "device released";
+    case FFERR_EFFECTPLAYING:
+        return "effect playing";
+    case FFERR_EFFECTTYPEMISMATCH:
+        return "effect type mismatch";
+    case FFERR_EFFECTTYPENOTSUPPORTED:
+        return "effect type not supported";
+    case FFERR_GENERIC:
+        return "undetermined error";
+    case FFERR_HASEFFECTS:
+        return "device has effects";
+    case FFERR_INCOMPLETEEFFECT:
+        return "incomplete effect";
+    case FFERR_INTERNAL:
+        return "internal fault";
+    case FFERR_INVALIDDOWNLOADID:
+        return "invalid download id";
+    case FFERR_INVALIDPARAM:
+        return "invalid parameter";
+    case FFERR_MOREDATA:
+        return "more data";
+    case FFERR_NOINTERFACE:
+        return "interface not supported";
+    case FFERR_NOTDOWNLOADED:
+        return "effect is not downloaded";
+    case FFERR_NOTINITIALIZED:
+        return "object has not been initialized";
+    case FFERR_OUTOFMEMORY:
+        return "out of memory";
+    case FFERR_UNPLUGGED:
+        return "device is unplugged";
+    case FFERR_UNSUPPORTED:
+        return "function call unsupported";
+    case FFERR_UNSUPPORTEDAXIS:
+        return "axis unsupported";
+
+    default:
+        return "unknown error";
+    }
 }
 
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static int
+DARWIN_JoystickInitRumble(recDevice *device, Sint16 magnitude, Uint32 duration_ms)
+{
+    HRESULT result;
+
+    if (!device->ffdevice) {
+        result = FFCreateDevice(device->ffservice, &device->ffdevice);
+        if (result != FF_OK) {
+            return SDL_SetError("Unable to create force feedback device from service: %s", FFStrError(result));
+        }
+    }
+
+    /* Reset and then enable actuators */
+    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_RESET);
+    if (result != FF_OK) {
+        return SDL_SetError("Unable to reset force feedback device: %s", FFStrError(result));
+    }
+
+    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_SETACTUATORSON);
+    if (result != FF_OK) {
+        return SDL_SetError("Unable to enable force feedback actuators: %s", FFStrError(result));
+    }
+
+    /* Create the effect */
+    device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
+    if (!device->ffeffect) {
+        return SDL_OutOfMemory();
+    }
+
+    result = FFDeviceCreateEffect(device->ffdevice, kFFEffectType_Sine_ID,
+                               device->ffeffect, &device->ffeffect_ref);
+    if (result != FF_OK) {
+        return SDL_SetError("Haptic: Unable to create effect: %s", FFStrError(result));
+    }
+    return 0;
+}
+
+static int
+DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    HRESULT result;
+    recDevice *device = joystick->hwdata;
+
+    /* Scale and average the two rumble strengths */
+    Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+
+    if (!device->ffservice) {
+        return SDL_Unsupported();
+    }
+
+    if (device->ff_initialized) {
+        FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
+        device->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
+        periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+
+        result = FFEffectSetParameters(device->ffeffect_ref, device->ffeffect,
+                                    (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
+        if (result != FF_OK) {
+            return SDL_SetError("Unable to update rumble effect: %s", FFStrError(result));
+        }
+    } else {
+        if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
+            return -1;
+        }
+        device->ff_initialized = SDL_TRUE;
+    }
+
+    result = FFEffectStart(device->ffeffect_ref, 1, 0);
+    if (result != FF_OK) {
+        return SDL_SetError("Unable to run the rumble effect: %s", FFStrError(result));
+    }
+    return 0;
+}
+
+static void
+DARWIN_JoystickUpdate(SDL_Joystick * joystick)
 {
     recDevice *device = joystick->hwdata;
     recElement *element;
@@ -792,15 +978,13 @@
     }
 }
 
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+DARWIN_JoystickClose(SDL_Joystick * joystick)
 {
 }
 
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+DARWIN_JoystickQuit(void)
 {
     while (FreeDevice(gpDeviceList)) {
         /* spin */
@@ -814,23 +998,21 @@
     }
 }
 
-
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
 {
-    recDevice *device = GetDeviceForIndex(device_index);
-    SDL_JoystickGUID guid;
-    if (device) {
-        guid = device->guid;
-    } else {
-        SDL_zero(guid);
-    }
-    return guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
-{
-    return joystick->hwdata->guid;
-}
+    DARWIN_JoystickInit,
+    DARWIN_JoystickGetCount,
+    DARWIN_JoystickDetect,
+    DARWIN_JoystickGetDeviceName,
+    DARWIN_JoystickGetDevicePlayerIndex,
+    DARWIN_JoystickGetDeviceGUID,
+    DARWIN_JoystickGetDeviceInstanceID,
+    DARWIN_JoystickOpen,
+    DARWIN_JoystickRumble,
+    DARWIN_JoystickUpdate,
+    DARWIN_JoystickClose,
+    DARWIN_JoystickQuit,
+};
 
 #endif /* SDL_JOYSTICK_IOKIT */
 
diff --git a/source/src/joystick/darwin/SDL_sysjoystick_c.h b/source/src/joystick/darwin/SDL_sysjoystick_c.h
index cde6a5c..2168f91 100644
--- a/source/src/joystick/darwin/SDL_sysjoystick_c.h
+++ b/source/src/joystick/darwin/SDL_sysjoystick_c.h
@@ -24,6 +24,8 @@
 #define SDL_JOYSTICK_IOKIT_H
 
 #include <IOKit/hid/IOHIDLib.h>
+#include <ForceFeedback/ForceFeedback.h>
+#include <ForceFeedback/ForceFeedbackConstants.h>
 
 struct recElement
 {
@@ -45,6 +47,10 @@
 {
     IOHIDDeviceRef deviceRef;   /* HIDManager device handle */
     io_service_t ffservice;     /* Interface for force feedback, 0 = no ff */
+    FFDeviceObjectReference ffdevice;
+    FFEFFECT *ffeffect;
+    FFEffectObjectReference ffeffect_ref;
+    SDL_bool ff_initialized;
 
     char product[256];          /* name of product */
     uint32_t usage;                 /* usage page from IOUSBHID Parser.h which defines general usage */
diff --git a/source/src/joystick/dummy/SDL_sysjoystick.c b/source/src/joystick/dummy/SDL_sysjoystick.c
index 3dd96a0..ce0965d 100644
--- a/source/src/joystick/dummy/SDL_sysjoystick.c
+++ b/source/src/joystick/dummy/SDL_sysjoystick.c
@@ -28,99 +28,92 @@
 #include "../SDL_sysjoystick.h"
 #include "../SDL_joystick_c.h"
 
-/* Function to scan the system for joysticks.
- * It should return 0, or -1 on an unrecoverable fatal error.
- */
-int
-SDL_SYS_JoystickInit(void)
+
+static int
+DUMMY_JoystickInit(void)
 {
     return 0;
 }
 
-int
-SDL_SYS_NumJoysticks(void)
+static int
+DUMMY_JoystickGetCount(void)
 {
     return 0;
 }
 
-void
-SDL_SYS_JoystickDetect(void)
+static void
+DUMMY_JoystickDetect(void)
 {
 }
 
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+DUMMY_JoystickGetDeviceName(int device_index)
 {
-    SDL_SetError("Logic error: No joysticks available");
-    return (NULL);
+    return NULL;
 }
 
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static int
+DUMMY_JoystickGetDevicePlayerIndex(int device_index)
 {
-    return device_index;
+    return -1;
 }
 
-/* Function to open a joystick for use.
-   The joystick to open is specified by the device index.
-   This should fill the nbuttons and naxes fields of the joystick structure.
-   It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static SDL_JoystickGUID
+DUMMY_JoystickGetDeviceGUID(int device_index)
+{
+    SDL_JoystickGUID guid;
+    SDL_zero(guid);
+    return guid;
+}
+
+static SDL_JoystickID
+DUMMY_JoystickGetDeviceInstanceID(int device_index)
+{
+    return -1;
+}
+
+static int
+DUMMY_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     return SDL_SetError("Logic error: No joysticks available");
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static int
+DUMMY_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
 {
-    return SDL_TRUE;
+    return SDL_Unsupported();
 }
 
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static void
+DUMMY_JoystickUpdate(SDL_Joystick * joystick)
 {
 }
 
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+DUMMY_JoystickClose(SDL_Joystick * joystick)
 {
 }
 
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+DUMMY_JoystickQuit(void)
 {
 }
 
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_DUMMY_JoystickDriver =
 {
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
-    const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
-    SDL_zero( guid );
-    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
-    return guid;
-}
-
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
-    const char *name = joystick->name;
-    SDL_zero( guid );
-    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
-    return guid;
-}
+    DUMMY_JoystickInit,
+    DUMMY_JoystickGetCount,
+    DUMMY_JoystickDetect,
+    DUMMY_JoystickGetDeviceName,
+    DUMMY_JoystickGetDevicePlayerIndex,
+    DUMMY_JoystickGetDeviceGUID,
+    DUMMY_JoystickGetDeviceInstanceID,
+    DUMMY_JoystickOpen,
+    DUMMY_JoystickRumble,
+    DUMMY_JoystickUpdate,
+    DUMMY_JoystickClose,
+    DUMMY_JoystickQuit,
+};
 
 #endif /* SDL_JOYSTICK_DUMMY || SDL_JOYSTICK_DISABLED */
 
diff --git a/source/src/joystick/emscripten/SDL_sysjoystick.c b/source/src/joystick/emscripten/SDL_sysjoystick.c
index b5bcaad..d551c8a 100644
--- a/source/src/joystick/emscripten/SDL_sysjoystick.c
+++ b/source/src/joystick/emscripten/SDL_sysjoystick.c
@@ -156,11 +156,34 @@
     return 1;
 }
 
+/* Function to perform any system-specific joystick related cleanup */
+static void
+EMSCRIPTEN_JoystickQuit(void)
+{
+    SDL_joylist_item *item = NULL;
+    SDL_joylist_item *next = NULL;
+
+    for (item = SDL_joylist; item; item = next) {
+        next = item->next;
+        SDL_free(item->mapping);
+        SDL_free(item->name);
+        SDL_free(item);
+    }
+
+    SDL_joylist = SDL_joylist_tail = NULL;
+
+    numjoysticks = 0;
+    instance_counter = 0;
+
+    emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
+    emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
+}
+
 /* Function to scan the system for joysticks.
  * It should return 0, or -1 on an unrecoverable fatal error.
  */
-int
-SDL_SYS_JoystickInit(void)
+static int
+EMSCRIPTEN_JoystickInit(void)
 {
     int retval, i, numjs;
     EmscriptenGamepadEvent gamepadState;
@@ -190,7 +213,7 @@
                                                       Emscripten_JoyStickConnected);
 
     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
-        SDL_SYS_JoystickQuit();
+        EMSCRIPTEN_JoystickQuit();
         return SDL_SetError("Could not set gamepad connect callback");
     }
 
@@ -198,7 +221,7 @@
                                                          0,
                                                          Emscripten_JoyStickDisconnected);
     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
-        SDL_SYS_JoystickQuit();
+        EMSCRIPTEN_JoystickQuit();
         return SDL_SetError("Could not set gamepad disconnect callback");
     }
 
@@ -239,26 +262,31 @@
     return item;
 }
 
-int
-SDL_SYS_NumJoysticks(void)
+static int
+EMSCRIPTEN_JoystickGetCount(void)
 {
     return numjoysticks;
 }
 
-void
-SDL_SYS_JoystickDetect(void)
+static void
+EMSCRIPTEN_JoystickDetect(void)
 {
 }
 
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+EMSCRIPTEN_JoystickGetDeviceName(int device_index)
 {
     return JoystickByDeviceIndex(device_index)->name;
 }
 
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static int
+EMSCRIPTEN_JoystickGetDevicePlayerIndex(int device_index)
+{
+    return -1;
+}
+
+static SDL_JoystickID
+EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index)
 {
     return JoystickByDeviceIndex(device_index)->device_instance;
 }
@@ -268,8 +296,8 @@
    This should fill the nbuttons and naxes fields of the joystick structure.
    It returns 0, or -1 if there is an error.
  */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+EMSCRIPTEN_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     SDL_joylist_item *item = JoystickByDeviceIndex(device_index);
 
@@ -295,19 +323,13 @@
     return (0);
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
-{
-    return joystick->hwdata != NULL;
-}
-
 /* Function to update the state of a joystick - called as a device poll.
  * This function shouldn't update the joystick structure directly,
  * but instead should call SDL_PrivateJoystick*() to deliver events
  * and update joystick device state.
  */
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static void
+EMSCRIPTEN_JoystickUpdate(SDL_Joystick * joystick)
 {
     EmscriptenGamepadEvent gamepadState;
     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
@@ -346,8 +368,8 @@
 }
 
 /* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+EMSCRIPTEN_JoystickClose(SDL_Joystick * joystick)
 {
     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
     if (item) {
@@ -355,49 +377,39 @@
     }
 }
 
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
-{
-    SDL_joylist_item *item = NULL;
-    SDL_joylist_item *next = NULL;
-
-    for (item = SDL_joylist; item; item = next) {
-        next = item->next;
-        SDL_free(item->mapping);
-        SDL_free(item->name);
-        SDL_free(item);
-    }
-
-    SDL_joylist = SDL_joylist_tail = NULL;
-
-    numjoysticks = 0;
-    instance_counter = 0;
-
-    emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
-    emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
-}
-
-SDL_JoystickGUID
-SDL_SYS_JoystickGetDeviceGUID(int device_index)
+static SDL_JoystickGUID
+EMSCRIPTEN_JoystickGetDeviceGUID(int device_index)
 {
     SDL_JoystickGUID guid;
     /* the GUID is just the first 16 chars of the name for now */
-    const char *name = SDL_SYS_JoystickNameForDeviceIndex(device_index);
+    const char *name = EMSCRIPTEN_JoystickGetDeviceName(device_index);
     SDL_zero(guid);
     SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
     return guid;
 }
 
-SDL_JoystickGUID
-SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
+static int
+EMSCRIPTEN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
 {
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
-    const char *name = joystick->name;
-    SDL_zero(guid);
-    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
-    return guid;
+    return SDL_Unsupported();
 }
+
+SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver =
+{
+    EMSCRIPTEN_JoystickInit,
+    EMSCRIPTEN_JoystickGetCount,
+    EMSCRIPTEN_JoystickDetect,
+    EMSCRIPTEN_JoystickGetDeviceName,
+    EMSCRIPTEN_JoystickGetDevicePlayerIndex,
+    EMSCRIPTEN_JoystickGetDeviceGUID,
+    EMSCRIPTEN_JoystickGetDeviceInstanceID,
+    EMSCRIPTEN_JoystickOpen,
+    EMSCRIPTEN_JoystickRumble,
+    EMSCRIPTEN_JoystickUpdate,
+    EMSCRIPTEN_JoystickClose,
+    EMSCRIPTEN_JoystickQuit,
+};
 
 #endif /* SDL_JOYSTICK_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/haiku/SDL_haikujoystick.cc b/source/src/joystick/haiku/SDL_haikujoystick.cc
index 9ab2c72..9fa8ca9 100644
--- a/source/src/joystick/haiku/SDL_haikujoystick.cc
+++ b/source/src/joystick/haiku/SDL_haikujoystick.cc
@@ -36,7 +36,7 @@
 
 
 /* The maximum number of joysticks we'll detect */
-#define MAX_JOYSTICKS	16
+#define MAX_JOYSTICKS   16
 
 /* A list of available joysticks */
     static char *SDL_joyport[MAX_JOYSTICKS];
@@ -50,13 +50,13 @@
         int16 *new_axes;
     };
 
-    static int SDL_SYS_numjoysticks = 0;
+    static int numjoysticks = 0;
 
 /* Function to scan the system for joysticks.
  * Joystick 0 should be the system default joystick.
  * It should return 0, or -1 on an unrecoverable fatal error.
  */
-    int SDL_SYS_JoystickInit(void)
+    static int HAIKU_JoystickInit(void)
     {
         BJoystick joystick;
         int i;
@@ -65,52 +65,59 @@
 
         /* Search for attached joysticks */
           nports = joystick.CountDevices();
-          SDL_SYS_numjoysticks = 0;
+          numjoysticks = 0;
           SDL_memset(SDL_joyport, 0, (sizeof SDL_joyport));
           SDL_memset(SDL_joyname, 0, (sizeof SDL_joyname));
-        for (i = 0; (SDL_SYS_numjoysticks < MAX_JOYSTICKS) && (i < nports); ++i)
+        for (i = 0; (numjoysticks < MAX_JOYSTICKS) && (i < nports); ++i)
         {
             if (joystick.GetDeviceName(i, name) == B_OK) {
                 if (joystick.Open(name) != B_ERROR) {
                     BString stick_name;
                       joystick.GetControllerName(&stick_name);
-                      SDL_joyport[SDL_SYS_numjoysticks] = SDL_strdup(name);
-                      SDL_joyname[SDL_SYS_numjoysticks] = SDL_strdup(stick_name.String());
-                      SDL_SYS_numjoysticks++;
+                      SDL_joyport[numjoysticks] = SDL_strdup(name);
+                      SDL_joyname[numjoysticks] = SDL_strdup(stick_name.String());
+                      numjoysticks++;
                       joystick.Close();
                 }
             }
         }
-        return (SDL_SYS_numjoysticks);
+        return (numjoysticks);
     }
 
-    int SDL_SYS_NumJoysticks(void)
+    static int HAIKU_JoystickGetCount(void)
     {
-        return SDL_SYS_numjoysticks;
+        return numjoysticks;
     }
 
-    void SDL_SYS_JoystickDetect(void)
+    static void HAIKU_JoystickDetect(void)
     {
     }
 
 /* Function to get the device-dependent name of a joystick */
-    const char *SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+    static const char *HAIKU_JoystickGetDeviceName(int device_index)
     {
         return SDL_joyname[device_index];
     }
 
+    static int HAIKU_JoystickGetDevicePlayerIndex(int device_index)
+    {
+        return -1;
+    }
+
 /* Function to perform the mapping from device index to the instance id for this index */
-    SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+    static SDL_JoystickID HAIKU_JoystickGetDeviceInstanceID(int device_index)
     {
         return device_index;
     }
+
+    static void HAIKU_JoystickClose(SDL_Joystick * joystick);
 
 /* Function to open a joystick for use.
    The joystick to open is specified by the device index.
    This should fill the nbuttons and naxes fields of the joystick structure.
    It returns 0, or -1 if there is an error.
  */
-    int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+    static int HAIKU_JoystickOpen(SDL_Joystick * joystick, int device_index)
     {
         BJoystick *stick;
 
@@ -127,7 +134,7 @@
 
         /* Open the requested joystick for use */
         if (stick->Open(SDL_joyport[device_index]) == B_ERROR) {
-            SDL_SYS_JoystickClose(joystick);
+            HAIKU_JoystickClose(joystick);
             return SDL_SetError("Unable to open joystick");
         }
 
@@ -144,18 +151,12 @@
         joystick->hwdata->new_hats = (uint8 *)
             SDL_malloc(joystick->nhats * sizeof(uint8));
         if (!joystick->hwdata->new_hats || !joystick->hwdata->new_axes) {
-            SDL_SYS_JoystickClose(joystick);
+            HAIKU_JoystickClose(joystick);
             return SDL_OutOfMemory();
         }
 
         /* We're done! */
-        return (0);
-    }
-
-/* Function to determine if this joystick is attached to the system right now */
-    SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
-    {
-        return SDL_TRUE;
+        return 0;
     }
 
 /* Function to update the state of a joystick - called as a device poll.
@@ -163,7 +164,7 @@
  * but instead should call SDL_PrivateJoystick*() to deliver events
  * and update joystick device state.
  */
-    void SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+    static void HAIKU_JoystickUpdate(SDL_Joystick * joystick)
     {
         static const Uint8 hat_map[9] = {
             SDL_HAT_CENTERED,
@@ -212,7 +213,7 @@
     }
 
 /* Function to close a joystick after use */
-    void SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+    static void HAIKU_JoystickClose(SDL_Joystick * joystick)
     {
         if (joystick->hwdata) {
             joystick->hwdata->stick->Close();
@@ -224,42 +225,53 @@
     }
 
 /* Function to perform any system-specific joystick related cleanup */
-    void SDL_SYS_JoystickQuit(void)
+    static void HAIKU_JoystickQuit(void)
     {
         int i;
 
-        for (i = 0; i < SDL_SYS_numjoysticks; ++i) {
+        for (i = 0; i < numjoysticks; ++i) {
             SDL_free(SDL_joyport[i]);
         }
         SDL_joyport[0] = NULL;
 
-        for (i = 0; i < SDL_SYS_numjoysticks; ++i) {
+        for (i = 0; i < numjoysticks; ++i) {
             SDL_free(SDL_joyname[i]);
         }
         SDL_joyname[0] = NULL;
     }
 
-    SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+    static SDL_JoystickGUID HAIKU_JoystickGetDeviceGUID( int device_index )
     {
         SDL_JoystickGUID guid;
         /* the GUID is just the first 16 chars of the name for now */
-        const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
+        const char *name = HAIKU_JoystickGetDeviceName( device_index );
         SDL_zero( guid );
         SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
         return guid;
     }
 
-    SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
+    static int HAIKU_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
     {
-        SDL_JoystickGUID guid;
-        /* the GUID is just the first 16 chars of the name for now */
-        const char *name = joystick->name;
-        SDL_zero( guid );
-        SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
-        return guid;
+        return SDL_Unsupported();
     }
 
-};                              // extern "C"
+    SDL_JoystickDriver SDL_HAIKU_JoystickDriver =
+    {
+        HAIKU_JoystickInit,
+        HAIKU_JoystickGetCount,
+        HAIKU_JoystickDetect,
+        HAIKU_JoystickGetDeviceName,
+        HAIKU_JoystickGetDevicePlayerIndex,
+        HAIKU_JoystickGetDeviceGUID,
+        HAIKU_JoystickGetDeviceInstanceID,
+        HAIKU_JoystickOpen,
+        HAIKU_JoystickRumble,
+        HAIKU_JoystickUpdate,
+        HAIKU_JoystickClose,
+        HAIKU_JoystickQuit,
+    };
+
+}                              // extern "C"
 
 #endif /* SDL_JOYSTICK_HAIKU */
 
diff --git a/source/src/joystick/hidapi/SDL_hidapi_ps4.c b/source/src/joystick/hidapi/SDL_hidapi_ps4.c
new file mode 100644
index 0000000..cdd478a
--- /dev/null
+++ b/source/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -0,0 +1,566 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+/* This driver supports both simplified reports and the extended input reports enabled by Steam.
+   Code and logic contributed by Valve Corporation under the SDL zlib license.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_PS4
+
+#define SONY_USB_VID        0x054C
+#define SONY_DS4_PID        0x05C4
+#define SONY_DS4_DONGLE_PID 0x0BA0
+#define SONY_DS4_SLIM_PID   0x09CC
+
+#define RAZER_USB_VID       0x1532
+#define RAZER_PANTHERA_PID  0X0401
+#define RAZER_PANTHERA_EVO_PID  0x1008
+
+#define USB_PACKET_LENGTH   64
+
+#define VOLUME_CHECK_INTERVAL_MS    (10 * 1000)
+
+typedef enum
+{
+    k_EPS4ReportIdUsbState = 1,
+    k_EPS4ReportIdUsbEffects = 5,
+    k_EPS4ReportIdBluetoothState = 17,
+    k_EPS4ReportIdBluetoothEffects = 17,
+    k_EPS4ReportIdDisconnectMessage = 226,
+} EPS4ReportId;
+
+typedef enum 
+{
+    k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
+    k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
+    k_ePS4FeatureReportIdSerialNumber = 0x12,
+} EPS4FeatureReportID;
+
+typedef struct
+{
+    Uint8 ucLeftJoystickX;
+    Uint8 ucLeftJoystickY;
+    Uint8 ucRightJoystickX;
+    Uint8 ucRightJoystickY;
+    Uint8 rgucButtonsHatAndCounter[ 3 ];
+    Uint8 ucTriggerLeft;
+    Uint8 ucTriggerRight;
+    Uint8 _rgucPad0[ 3 ];
+    Sint16 sGyroX;
+    Sint16 sGyroY;
+    Sint16 sGyroZ;
+    Sint16 sAccelX;
+    Sint16 sAccelY;
+    Sint16 sAccelZ;
+    Uint8 _rgucPad1[ 5 ];
+    Uint8 ucBatteryLevel;
+    Uint8 _rgucPad2[ 4 ];
+    Uint8 ucTrackpadCounter1;
+    Uint8 rgucTrackpadData1[ 3 ];
+    Uint8 ucTrackpadCounter2;
+    Uint8 rgucTrackpadData2[ 3 ];
+} PS4StatePacket_t;
+
+typedef struct
+{
+    Uint8 ucRumbleRight;
+    Uint8 ucRumbleLeft;
+    Uint8 ucLedRed;
+    Uint8 ucLedGreen;
+    Uint8 ucLedBlue;
+    Uint8 ucLedDelayOn;
+    Uint8 ucLedDelayOff;
+    Uint8 _rgucPad0[ 8 ];
+    Uint8 ucVolumeLeft;
+    Uint8 ucVolumeRight;
+    Uint8 ucVolumeMic;
+    Uint8 ucVolumeSpeaker;
+} DS4EffectsState_t;
+
+typedef struct {
+    SDL_bool is_dongle;
+    SDL_bool is_bluetooth;
+    SDL_bool audio_supported;
+    SDL_bool rumble_supported;
+    Uint8 volume;
+    Uint32 last_volume_check;
+    Uint32 rumble_expiration;
+    PS4StatePacket_t last_state;
+} SDL_DriverPS4_Context;
+
+
+/* Public domain CRC implementation adapted from:
+   http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
+*/
+static Uint32 crc32_for_byte(Uint32 r)
+{
+    int i;
+    for(i = 0; i < 8; ++i) {
+        r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
+    }
+    return r ^ (Uint32)0xFF000000L;
+}
+
+static Uint32 crc32(Uint32 crc, const void *data, int count)
+{
+    int i;
+    for(i = 0; i < count; ++i) {
+        crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
+    }
+    return crc;
+}
+
+#if defined(__WIN32__) && defined(HAVE_ENDPOINTVOLUME_H)
+#include "../../core/windows/SDL_windows.h"
+
+#ifndef NTDDI_VISTA
+#define NTDDI_VISTA    0x06000000
+#endif
+#ifndef _WIN32_WINNT_VISTA
+#define _WIN32_WINNT_VISTA 0x0600
+#endif
+
+/* Define Vista for the Audio related includes below to work */
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_VISTA
+#undef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#define COBJMACROS
+#include <mmdeviceapi.h>
+#include <audioclient.h>
+#include <endpointvolume.h>
+
+#undef DEFINE_GUID
+#define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
+DEFINE_GUID(SDL_CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
+DEFINE_GUID(SDL_IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
+DEFINE_GUID(SDL_IID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A);
+#endif
+
+
+
+static float GetSystemVolume(void)
+{
+    float volume = -1.0f;    /* Return this if we can't get system volume */
+
+#if defined(__WIN32__) && defined(HAVE_ENDPOINTVOLUME_H)
+    HRESULT hr = WIN_CoInitialize();
+    if (SUCCEEDED(hr)) {
+        IMMDeviceEnumerator *pEnumerator;
+
+        /* This should gracefully fail on XP and succeed on everything Vista and above */
+        hr = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &SDL_IID_IMMDeviceEnumerator, (LPVOID*)&pEnumerator);
+        if (SUCCEEDED(hr)) {
+            IMMDevice *pDevice;
+
+            hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
+            if (SUCCEEDED(hr)) {
+                IAudioEndpointVolume *pEndpointVolume;
+
+                hr = IMMDevice_Activate(pDevice, &SDL_IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (LPVOID*)&pEndpointVolume);
+                if (SUCCEEDED(hr)) {
+                    IAudioEndpointVolume_GetMasterVolumeLevelScalar(pEndpointVolume, &volume);
+                    IUnknown_Release(pEndpointVolume);
+                }
+                IUnknown_Release(pDevice);
+            }
+            IUnknown_Release(pEnumerator);
+        }
+        WIN_CoUninitialize();
+    }
+#endif /* __WIN32__ */
+
+    return volume;
+}
+
+static uint8_t GetPlaystationVolumeFromFloat(float fVolume)
+{
+    const int k_nVolumeFitRatio = 15;
+    const int k_nVolumeFitOffset = 9;
+    float fVolLog;
+
+    if (fVolume > 1.0f || fVolume < 0.0f) {
+        fVolume = 0.30f;
+    }
+    fVolLog = SDL_logf(fVolume * 100);
+
+    return (Uint8)((fVolLog * k_nVolumeFitRatio) + k_nVolumeFitOffset);
+}
+
+static SDL_bool
+HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
+{
+    return SDL_IsJoystickPS4(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+    if (vendor_id == SONY_USB_VID) {
+        return "PS4 Controller";
+    }
+    return NULL;
+}
+
+static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
+{
+    Uint8 report[USB_PACKET_LENGTH + 1];
+
+    SDL_memset(report, 0, sizeof(report));
+    report[0] = report_id;
+    if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
+        return SDL_FALSE;
+    }
+    SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
+    return SDL_TRUE;
+}
+
+static SDL_bool CheckUSBConnected(hid_device *dev)
+{
+    int i;
+    Uint8 data[16];
+
+    /* This will fail if we're on Bluetooth */
+    if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
+        for (i = 0; i < sizeof(data); ++i) {
+            if (data[i] != 0x00) {
+                return SDL_TRUE;
+            }
+        }
+        /* Maybe the dongle without a connected controller? */
+    }
+    return SDL_FALSE;
+}
+
+static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
+{
+    /* The Razer Panthera fight stick hangs when trying to rumble */
+    if (vendor_id == RAZER_USB_VID &&
+        (product_id == RAZER_PANTHERA_PID || product_id == RAZER_PANTHERA_EVO_PID)) {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
+static int HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
+static SDL_bool
+HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+    SDL_DriverPS4_Context *ctx;
+
+    ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    }
+    *context = ctx;
+
+    /* Check for type of connection */
+    ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID);
+    if (ctx->is_dongle) {
+        ctx->is_bluetooth = SDL_FALSE;
+    } else if (vendor_id == SONY_USB_VID) {
+        ctx->is_bluetooth = !CheckUSBConnected(dev);
+    } else {
+        /* Third party controllers appear to all be wired */
+        ctx->is_bluetooth = SDL_FALSE;
+    }
+#ifdef DEBUG_PS4
+    SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
+#endif
+
+    /* Check to see if audio is supported */
+    if (vendor_id == SONY_USB_VID &&
+        (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) {
+        ctx->audio_supported = SDL_TRUE;
+    }
+
+    if (HIDAPI_DriverPS4_CanRumble(vendor_id, product_id)) {
+        if (ctx->is_bluetooth) {
+            ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
+        } else {
+            ctx->rumble_supported = SDL_TRUE;
+        }
+    }
+
+    /* Initialize LED and effect state */
+    HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0);
+
+    /* Initialize the joystick capabilities */
+    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+    return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
+    DS4EffectsState_t *effects;
+    Uint8 data[78];
+    int report_size, offset;
+
+    if (!ctx->rumble_supported) {
+        return SDL_Unsupported();
+    }
+
+    /* In order to send rumble, we have to send a complete effect packet */
+    SDL_memset(data, 0, sizeof(data));
+
+    if (ctx->is_bluetooth) {
+        data[0] = k_EPS4ReportIdBluetoothEffects;
+        data[1] = 0xC0 | 0x04;  /* Magic value HID + CRC, also sets interval to 4ms for samples */
+        data[3] = 0x03;  /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
+
+        report_size = 78;
+        offset = 6;
+    } else {
+        data[0] = k_EPS4ReportIdUsbEffects;
+        data[1] = 0x07;  /* Magic value */
+
+        report_size = 32;
+        offset = 4;
+    }
+    effects = (DS4EffectsState_t *)&data[offset];
+
+    effects->ucRumbleLeft = (low_frequency_rumble >> 8);
+    effects->ucRumbleRight = (high_frequency_rumble >> 8);
+
+    effects->ucLedRed = 0;
+    effects->ucLedGreen = 0;
+    effects->ucLedBlue = 80;
+
+    if (ctx->audio_supported) {
+        Uint32 now = SDL_GetTicks();
+        if (!ctx->last_volume_check ||
+            SDL_TICKS_PASSED(now, ctx->last_volume_check + VOLUME_CHECK_INTERVAL_MS)) {
+            ctx->volume = GetPlaystationVolumeFromFloat(GetSystemVolume());
+            ctx->last_volume_check = now;
+        }
+
+        effects->ucVolumeRight = ctx->volume;
+        effects->ucVolumeLeft = ctx->volume;
+        effects->ucVolumeSpeaker = ctx->volume;
+        effects->ucVolumeMic = 0xFF;
+    }
+
+    if (ctx->is_bluetooth) {
+        /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
+        Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
+        Uint32 unCRC;
+        unCRC = crc32(0, &ubHdr, 1);
+        unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
+        SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
+    }
+
+    if (hid_write(dev, data, report_size) != report_size) {
+        return SDL_SetError("Couldn't send rumble packet");
+    }
+
+    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+        ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
+    } else {
+        ctx->rumble_expiration = 0;
+    }
+    return 0;
+}
+
+static void
+HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
+{
+    Sint16 axis;
+
+    if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
+        {
+            Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
+
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        }
+        {
+            Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
+            SDL_bool dpad_up = SDL_FALSE;
+            SDL_bool dpad_down = SDL_FALSE;
+            SDL_bool dpad_left = SDL_FALSE;
+            SDL_bool dpad_right = SDL_FALSE;
+
+            switch (data) {
+            case 0:
+                dpad_up = SDL_TRUE;
+                break;
+            case 1:
+                dpad_up = SDL_TRUE;
+                dpad_right = SDL_TRUE;
+                break;
+            case 2:
+                dpad_right = SDL_TRUE;
+                break;
+            case 3:
+                dpad_right = SDL_TRUE;
+                dpad_down = SDL_TRUE;
+                break;
+            case 4:
+                dpad_down = SDL_TRUE;
+                break;
+            case 5:
+                dpad_left = SDL_TRUE;
+                dpad_down = SDL_TRUE;
+                break;
+            case 6:
+                dpad_left = SDL_TRUE;
+                break;
+            case 7:
+                dpad_up = SDL_TRUE;
+                dpad_left = SDL_TRUE;
+                break;
+            default:
+                break;
+            }
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
+        }
+    }
+
+    if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
+        Uint8 data = packet->rgucButtonsHatAndCounter[1];
+
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
+        Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
+
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    axis = ((int)packet->ucTriggerLeft * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+    axis = ((int)packet->ucTriggerRight * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+    axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+    axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+    axis = ((int)packet->ucRightJoystickX * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+    axis = ((int)packet->ucRightJoystickY * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+    if (packet->ucBatteryLevel & 0x10) {
+        joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+    } else {
+        /* Battery level ranges from 0 to 10 */
+        int level = (packet->ucBatteryLevel & 0xF);
+        if (level == 0) {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
+        } else if (level <= 2) {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
+        } else if (level <= 7) {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
+        } else {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
+        }
+    }
+
+    SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
+}
+
+static SDL_bool
+HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
+    Uint8 data[USB_PACKET_LENGTH];
+    int size;
+
+    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
+        switch (data[0]) {
+        case k_EPS4ReportIdUsbState:
+            HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[1]);
+            break;
+        case k_EPS4ReportIdBluetoothState:
+            /* Bluetooth state packets have two additional bytes at the beginning */
+            HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[3]);
+            break;
+        default:
+#ifdef DEBUG_JOYSTICK
+            SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
+#endif
+            break;
+        }
+    }
+
+    if (ctx->rumble_expiration) {
+        Uint32 now = SDL_GetTicks();
+        if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
+            HIDAPI_DriverPS4_Rumble(joystick, dev, context, 0, 0, 0);
+        }
+    }
+
+    return (size >= 0);
+}
+
+static void
+HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
+{
+    SDL_HINT_JOYSTICK_HIDAPI_PS4,
+    SDL_TRUE,
+    HIDAPI_DriverPS4_IsSupportedDevice,
+    HIDAPI_DriverPS4_GetDeviceName,
+    HIDAPI_DriverPS4_Init,
+    HIDAPI_DriverPS4_Rumble,
+    HIDAPI_DriverPS4_Update,
+    HIDAPI_DriverPS4_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_PS4 */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/hidapi/SDL_hidapi_switch.c b/source/src/joystick/hidapi/SDL_hidapi_switch.c
new file mode 100644
index 0000000..16e4ea3
--- /dev/null
+++ b/source/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -0,0 +1,905 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+/* This driver supports the Nintendo Switch Pro controller.
+   Code and logic contributed by Valve Corporation under the SDL zlib license.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
+
+typedef enum {
+    k_eSwitchInputReportIDs_SubcommandReply       = 0x21,
+    k_eSwitchInputReportIDs_FullControllerState   = 0x30,
+    k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
+    k_eSwitchInputReportIDs_CommandAck            = 0x81,
+} ESwitchInputReportIDs;
+
+typedef enum {
+    k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
+    k_eSwitchOutputReportIDs_Rumble              = 0x10,
+    k_eSwitchOutputReportIDs_Proprietary         = 0x80,
+} ESwitchOutputReportIDs;
+
+typedef enum {
+    k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
+    k_eSwitchSubcommandIDs_RequestDeviceInfo   = 0x02,
+    k_eSwitchSubcommandIDs_SetInputReportMode  = 0x03,
+    k_eSwitchSubcommandIDs_SetHCIState         = 0x06,
+    k_eSwitchSubcommandIDs_SPIFlashRead        = 0x10,
+    k_eSwitchSubcommandIDs_SetPlayerLights     = 0x30,
+    k_eSwitchSubcommandIDs_SetHomeLight        = 0x38,
+    k_eSwitchSubcommandIDs_EnableIMU           = 0x40,
+    k_eSwitchSubcommandIDs_SetIMUSensitivity   = 0x41,
+    k_eSwitchSubcommandIDs_EnableVibration     = 0x48,
+} ESwitchSubcommandIDs;
+
+typedef enum {
+    k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
+    k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
+    k_eSwitchProprietaryCommandIDs_ForceUSB  = 0x04,
+    k_eSwitchProprietaryCommandIDs_ClearUSB  = 0x05,
+    k_eSwitchProprietaryCommandIDs_ResetMCU  = 0x06,
+} ESwitchProprietaryCommandIDs;
+
+typedef enum {
+    k_eSwitchDeviceInfoControllerType_JoyConLeft     = 0x1,
+    k_eSwitchDeviceInfoControllerType_JoyConRight    = 0x2,
+    k_eSwitchDeviceInfoControllerType_ProController  = 0x3,
+} ESwitchDeviceInfoControllerType;
+
+#define k_unSwitchOutputPacketDataLength 49
+#define k_unSwitchMaxOutputPacketLength  64
+#define k_unSwitchBluetoothPacketLength  k_unSwitchOutputPacketDataLength
+#define k_unSwitchUSBPacketLength        k_unSwitchMaxOutputPacketLength
+
+#define k_unSPIStickCalibrationStartOffset  0x603D
+#define k_unSPIStickCalibrationEndOffset    0x604E
+#define k_unSPIStickCalibrationLength       (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
+
+#pragma pack(1)
+typedef struct
+{
+    Uint8 rgucButtons[2];
+    Uint8 ucStickHat;
+    Sint16 sJoystickLeft[2];
+    Sint16 sJoystickRight[2];
+} SwitchSimpleStatePacket_t;
+
+typedef struct
+{
+    Uint8 ucCounter;
+    Uint8 ucBatteryAndConnection;
+    Uint8 rgucButtons[3];
+    Uint8 rgucJoystickLeft[3];
+    Uint8 rgucJoystickRight[3];
+    Uint8 ucVibrationCode;
+} SwitchControllerStatePacket_t;
+
+typedef struct
+{
+    SwitchControllerStatePacket_t controllerState;
+
+    struct {
+        Sint16 sAccelX;
+        Sint16 sAccelY;
+        Sint16 sAccelZ;
+
+        Sint16 sGyroX;
+        Sint16 sGyroY;
+        Sint16 sGyroZ;
+    } imuState[3];
+} SwitchStatePacket_t;
+
+typedef struct
+{
+    Uint32 unAddress;
+    Uint8 ucLength;
+} SwitchSPIOpData_t;
+
+typedef struct
+{
+    SwitchControllerStatePacket_t m_controllerState;
+
+    Uint8 ucSubcommandAck;
+    Uint8 ucSubcommandID;
+
+    #define k_unSubcommandDataBytes 35
+    union {
+        Uint8 rgucSubcommandData[ k_unSubcommandDataBytes ];
+
+        struct {
+            SwitchSPIOpData_t opData;
+            Uint8 rgucReadData[ k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t) ];
+        } spiReadData;
+
+        struct {
+            Uint8 rgucFirmwareVersion[2];
+            Uint8 ucDeviceType;
+            Uint8 ucFiller1;
+            Uint8 rgucMACAddress[6];
+            Uint8 ucFiller2;
+            Uint8 ucColorLocation;
+        } deviceInfo;
+    };
+} SwitchSubcommandInputPacket_t;
+
+typedef struct
+{
+    Uint8 rgucData[4];
+} SwitchRumbleData_t;
+
+typedef struct
+{
+    Uint8 ucPacketType;
+    Uint8 ucPacketNumber;
+    SwitchRumbleData_t rumbleData[2];
+} SwitchCommonOutputPacket_t;
+
+typedef struct
+{
+    SwitchCommonOutputPacket_t commonData;
+
+    Uint8 ucSubcommandID;
+    Uint8 rgucSubcommandData[ k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1 ];
+} SwitchSubcommandOutputPacket_t;
+
+typedef struct
+{
+    Uint8 ucPacketType;
+    Uint8 ucProprietaryID;
+
+    Uint8 rgucProprietaryData[ k_unSwitchOutputPacketDataLength - 1 - 1 ];
+} SwitchProprietaryOutputPacket_t;
+#pragma pack()
+
+typedef struct {
+    hid_device *dev;
+    SDL_bool m_bIsUsingBluetooth;
+    Uint8 m_nCommandNumber;
+    SwitchCommonOutputPacket_t m_RumblePacket;
+    Uint32 m_nRumbleExpiration;
+    Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
+    SwitchSimpleStatePacket_t m_lastSimpleState;
+    SwitchStatePacket_t m_lastFullState;
+
+    struct StickCalibrationData {
+        struct {
+            Sint16 sCenter;
+            Sint16 sMin;
+            Sint16 sMax;
+        } axis[2];
+    } m_StickCalData[2];
+
+    struct StickExtents {
+        struct {
+            Sint16 sMin;
+            Sint16 sMax;
+        } axis[2];
+    } m_StickExtents[2];
+} SDL_DriverSwitch_Context;
+
+
+static SDL_bool
+HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
+{
+    return SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+    /* Give a user friendly name for this controller */
+    if (SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id)) {
+        return "Nintendo Switch Pro Controller";
+    }
+    return NULL;
+}
+
+static int ReadInput(SDL_DriverSwitch_Context *ctx)
+{
+    return hid_read_timeout(ctx->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
+}
+
+static int WriteOutput(SDL_DriverSwitch_Context *ctx, Uint8 *data, int size)
+{
+    return hid_write(ctx->dev, data, size);
+}
+
+static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
+{
+    /* Average response time for messages is ~30ms */
+    Uint32 TimeoutMs = 100;
+    Uint32 startTicks = SDL_GetTicks();
+
+    int nRead = 0;
+    while ((nRead = ReadInput(ctx)) != -1) {
+        if (nRead > 0) {
+            if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
+                SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[ 1 ];
+                if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
+                    return reply;
+                }
+            }
+        } else {
+            SDL_Delay(1);
+        }
+
+        if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
+            break;
+        }
+    }
+    return NULL;
+}
+
+static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
+{
+    /* Average response time for messages is ~30ms */
+    Uint32 TimeoutMs = 100;
+    Uint32 startTicks = SDL_GetTicks();
+
+    int nRead = 0;
+    while ((nRead = ReadInput(ctx)) != -1) {
+        if (nRead > 0) {
+            if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[ 1 ] == expectedID) {
+                return SDL_TRUE;
+            }
+        } else {
+            SDL_Delay(1);
+        }
+
+        if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
+            break;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
+{
+    SDL_memset(outPacket, 0, sizeof(*outPacket));
+
+    outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
+    outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
+
+    SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
+
+    outPacket->ucSubcommandID = ucCommandID;
+    SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
+
+    ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
+}
+
+static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
+{
+    Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
+    const size_t unWriteSize = ctx->m_bIsUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
+
+    if (ucLen > k_unSwitchOutputPacketDataLength) {
+        return SDL_FALSE;
+    }
+
+    if (ucLen < unWriteSize) {
+        SDL_memcpy(rgucBuf, pBuf, ucLen);
+        SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
+        pBuf = rgucBuf;
+        ucLen = (Uint8)unWriteSize;
+    }
+    return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
+}
+
+static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
+{
+    int nRetries = 5;
+    SwitchSubcommandInputPacket_t *reply = NULL;
+
+    while (!reply && nRetries--) {
+        SwitchSubcommandOutputPacket_t commandPacket;
+        ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
+
+        if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
+            continue;
+        }
+
+        reply = ReadSubcommandReply(ctx, ucCommandID);
+    }
+
+    if (ppReply) {
+        *ppReply = reply;
+    }
+    return reply != NULL;
+}
+
+static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
+{
+    int nRetries = 5;
+
+    while (nRetries--) {
+        SwitchProprietaryOutputPacket_t packet;
+
+        if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
+            return SDL_FALSE;
+        }
+
+        packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
+        packet.ucProprietaryID = ucCommand;
+        SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
+
+        if (!WritePacket(ctx, &packet, sizeof(packet))) {
+            continue;
+        }
+
+        if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
+{
+    pRumble->rgucData[0] = 0x00;
+    pRumble->rgucData[1] = 0x01;
+    pRumble->rgucData[2] = 0x40;
+    pRumble->rgucData[3] = 0x40;
+}
+
+static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
+{
+    if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
+        // High-band frequency and low-band amplitude are actually nine-bits each so they
+        // take a bit from the high-band amplitude and low-band frequency bytes respectively
+        pRumble->rgucData[0] = usHighFreq & 0xFF;
+        pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
+
+        pRumble->rgucData[2]  = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
+        pRumble->rgucData[3]  = usLowFreqAmp & 0xFF;
+
+#ifdef DEBUG_RUMBLE
+        SDL_Log("Freq: %.2X %.2X  %.2X, Amp: %.2X  %.2X %.2X\n",
+            usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
+            ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
+#endif
+    } else {
+        SetNeutralRumble(pRumble);
+    }
+}
+
+static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
+{
+    /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
+     * to be retained for subsequent rumble or subcommand packets sent to the controller
+     */
+    ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
+    ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
+    ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
+
+    return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
+}
+
+static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
+{
+    /* We have to send a connection handshake to the controller when communicating over USB
+     * before we're able to send it other commands. Luckily this command is not supported
+     * over Bluetooth, so we can use the controller's lack of response as a way to
+     * determine if the connection is over USB or Bluetooth
+     */
+    if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
+        return SDL_FALSE;
+    }
+    if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) {
+        return SDL_FALSE;
+    }
+    if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
+static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
+{
+    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
+
+}
+static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
+{
+    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL);
+}
+
+static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
+{
+    Uint8 ucLedIntensity = 0;
+    Uint8 rgucBuffer[4];
+
+    if (brightness > 0) {
+        if (brightness < 65) {
+            ucLedIntensity = (brightness + 5) / 10;
+        } else {
+            ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
+        }
+    }
+
+    rgucBuffer[0] = (0x0 << 4) | 0x1;  /* 0 mini cycles (besides first), cycle duration 8ms */
+    rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0;  /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */
+    rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0;  /* First cycle LED intensity, 0x0 intensity for second cycle */
+    rgucBuffer[3] = (0x0 << 4) | 0x0;  /* 8ms fade transition to first cycle, 8ms first cycle LED duration */
+
+    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
+}
+
+static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
+{
+    Uint8 led_data = (1 << slot);
+    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
+}
+
+static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
+{
+    Uint8 *pStickCal;
+    size_t stick, axis;
+    SwitchSubcommandInputPacket_t *reply = NULL;
+
+    /* Read Calibration Info */
+    SwitchSPIOpData_t readParams;
+    readParams.unAddress = k_unSPIStickCalibrationStartOffset;
+    readParams.ucLength = k_unSPIStickCalibrationLength;
+
+    if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
+        return SDL_FALSE;
+    }
+
+    /* Stick calibration values are 12-bits each and are packed by bit
+     * For whatever reason the fields are in a different order for each stick
+     * Left:  X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
+     * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
+     */
+    pStickCal = reply->spiReadData.rgucReadData;
+
+    /* Left stick */
+    ctx->m_StickCalData[0].axis[0].sMax    = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0];     /* X Axis max above center */
+    ctx->m_StickCalData[0].axis[1].sMax    = (pStickCal[2] << 4) | (pStickCal[1] >> 4);         /* Y Axis max above center */
+    ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3];     /* X Axis center */
+    ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4);        /* Y Axis center */
+    ctx->m_StickCalData[0].axis[0].sMin    = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6];      /* X Axis min below center */
+    ctx->m_StickCalData[0].axis[1].sMin    = (pStickCal[8] << 4) | (pStickCal[7] >> 4);        /* Y Axis min below center */
+
+    /* Right stick */
+    ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9];     /* X Axis center */
+    ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4);      /* Y Axis center */
+    ctx->m_StickCalData[1].axis[0].sMin    = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12];    /* X Axis min below center */
+    ctx->m_StickCalData[1].axis[1].sMin    = (pStickCal[14] << 4) | (pStickCal[13] >> 4);      /* Y Axis min below center */
+    ctx->m_StickCalData[1].axis[0].sMax    = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15];    /* X Axis max above center */
+    ctx->m_StickCalData[1].axis[1].sMax    = (pStickCal[17] << 4) | (pStickCal[16] >> 4);      /* Y Axis max above center */
+
+    /* Filter out any values that were uninitialized (0xFFF) in the SPI read */
+    for (stick = 0; stick < 2; ++stick) {
+        for (axis = 0; axis < 2; ++axis) {
+            if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
+                ctx->m_StickCalData[stick].axis[axis].sCenter = 0;
+            }
+            if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
+                ctx->m_StickCalData[stick].axis[axis].sMax = 0;
+            }
+            if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
+                ctx->m_StickCalData[stick].axis[axis].sMin = 0;
+            }
+        }
+    }
+
+    if (ctx->m_bIsUsingBluetooth) {
+        for (stick = 0; stick < 2; ++stick) {
+            for(axis = 0; axis < 2; ++axis) {
+                ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
+                ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
+            }
+        }
+    } else {
+        for (stick = 0; stick < 2; ++stick) {
+            for(axis = 0; axis < 2; ++axis) {
+                ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
+                ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
+            }
+        }
+    }
+    return SDL_TRUE;
+}
+
+static float fsel(float fComparand, float fValGE, float fLT)
+{
+    return fComparand >= 0 ? fValGE : fLT;
+}
+
+static float RemapVal(float val, float A, float B, float C, float D)
+{
+    if (A == B) {
+        return fsel(val - B , D , C);
+    }
+    return C + (D - C) * (val - A) / (B - A);
+}
+
+static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter)
+{
+    sRawValue -= sCenter;
+
+    if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
+        ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
+    }
+    if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
+        ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
+    }
+
+    if (sRawValue > 0) {
+        return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16));
+    } else {
+        return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0));
+    }
+}
+
+static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
+{
+    return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
+}
+
+static SDL_bool
+HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+    SDL_DriverSwitch_Context *ctx;
+    Uint8 input_mode;
+
+    ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    }
+    ctx->dev = dev;
+
+    *context = ctx;
+
+    /* Initialize rumble data */
+    SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
+    SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
+
+    /* Try setting up USB mode, and if that fails we're using Bluetooth */
+    if (!BTrySetupUSB(ctx)) {
+        ctx->m_bIsUsingBluetooth = SDL_TRUE;
+    }
+
+    if (!LoadStickCalibration(ctx)) {
+        SDL_SetError("Couldn't load stick calibration");
+        SDL_free(ctx);
+        return SDL_FALSE;
+    }
+
+    if (!SetVibrationEnabled(ctx, 1)) {
+        SDL_SetError("Couldn't enable vibration");
+        SDL_free(ctx);
+        return SDL_FALSE;
+    }
+
+    /* Set the desired input mode */
+    if (ctx->m_bIsUsingBluetooth) {
+        input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
+    } else {
+        input_mode = k_eSwitchInputReportIDs_FullControllerState;
+    }
+    if (!SetInputMode(ctx, input_mode)) {
+        SDL_SetError("Couldn't set input mode");
+        SDL_free(ctx);
+        return SDL_FALSE;
+    }
+
+    /* Start sending USB reports */
+    if (!ctx->m_bIsUsingBluetooth) {
+        /* ForceUSB doesn't generate an ACK, so don't wait for a reply */
+        if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
+            SDL_SetError("Couldn't start USB reports");
+            SDL_free(ctx);
+            return SDL_FALSE;
+        }
+    }
+
+    /* Set the LED state */
+    SetHomeLED(ctx, 100);
+    SetSlotLED(ctx, (joystick->instance_id % 4));
+
+    /* Initialize the joystick capabilities */
+    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+    return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
+
+    /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
+     * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
+     *
+     * More information about these values can be found here:
+     * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
+     */
+    const Uint16 k_usHighFreq = 0x0074;
+    const Uint8  k_ucHighFreqAmp = 0xBE;
+    const Uint8  k_ucLowFreq = 0x3D;
+    const Uint16 k_usLowFreqAmp = 0x806F;
+
+    if (low_frequency_rumble) {
+        EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
+    } else {
+        SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
+    }
+
+    if (high_frequency_rumble) {
+        EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
+    } else {
+        SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
+    }
+
+    if (!WriteRumble(ctx)) {
+        SDL_SetError("Couldn't send rumble packet");
+        return -1;
+    }
+
+    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+        ctx->m_nRumbleExpiration = SDL_GetTicks() + duration_ms;
+    } else {
+        ctx->m_nRumbleExpiration = 0;
+    }
+    return 0;
+}
+
+static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
+{
+    /* 0x8000 is the neutral value for all joystick axes */
+    const Uint16 usJoystickCenter = 0x8000;
+    Sint16 axis;
+
+    if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
+        Uint8 data = packet->rgucButtons[0];
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+
+        axis = (data & 0x40) ? 32767 : -32768;
+        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+
+        axis = (data & 0x80) ? 32767 : -32768;
+        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+    }
+
+    if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
+        Uint8 data = packet->rgucButtons[1];
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
+        SDL_bool dpad_up = SDL_FALSE;
+        SDL_bool dpad_down = SDL_FALSE;
+        SDL_bool dpad_left = SDL_FALSE;
+        SDL_bool dpad_right = SDL_FALSE;
+
+        switch (packet->ucStickHat) {
+        case 0:
+            dpad_up = SDL_TRUE;
+            break;
+        case 1:
+            dpad_up = SDL_TRUE;
+            dpad_right = SDL_TRUE;
+            break;
+        case 2:
+            dpad_right = SDL_TRUE;
+            break;
+        case 3:
+            dpad_right = SDL_TRUE;
+            dpad_down = SDL_TRUE;
+            break;
+        case 4:
+            dpad_down = SDL_TRUE;
+            break;
+        case 5:
+            dpad_left = SDL_TRUE;
+            dpad_down = SDL_TRUE;
+            break;
+        case 6:
+            dpad_left = SDL_TRUE;
+            break;
+        case 7:
+            dpad_up = SDL_TRUE;
+            dpad_left = SDL_TRUE;
+            break;
+        default:
+            break;
+        }
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
+    }
+
+    axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+
+    axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+
+    axis = ApplyStickCalibrationCentered(ctx, 1, 0, packet->sJoystickRight[0], (Sint16)usJoystickCenter);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+
+    axis = ApplyStickCalibrationCentered(ctx, 1, 1, packet->sJoystickRight[1], (Sint16)usJoystickCenter);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+    ctx->m_lastSimpleState = *packet;
+}
+
+static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
+{
+    Sint16 axis;
+
+    if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
+        Uint8 data = packet->controllerState.rgucButtons[0];
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        axis = (data & 0x80) ? 32767 : -32768;
+        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+    }
+
+    if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
+        Uint8 data = packet->controllerState.rgucButtons[1];
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
+        Uint8 data = packet->controllerState.rgucButtons[2];
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        axis = (data & 0x80) ? 32767 : -32768;
+        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+    }
+
+    axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
+    axis = ApplyStickCalibration(ctx, 0, 0, axis);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+
+    axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
+    axis = ApplyStickCalibration(ctx, 0, 1, axis);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
+
+    axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
+    axis = ApplyStickCalibration(ctx, 1, 0, axis);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+
+    axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
+    axis = ApplyStickCalibration(ctx, 1, 1, axis);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
+
+    /* High nibble of battery/connection byte is battery level, low nibble is connection status
+     * LSB of connection nibble is USB/Switch connection status
+     */
+    if (packet->controllerState.ucBatteryAndConnection & 0x1) {
+        joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+    } else {
+        /* LSB of the battery nibble is used to report charging.
+         * The battery level is reported from 0(empty)-8(full)
+         */
+        int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
+        if (level == 0) {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
+        } else if (level <= 2) {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
+        } else if (level <= 6) {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
+        } else {
+            joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
+        }
+    }
+
+    ctx->m_lastFullState = *packet;
+}
+
+static SDL_bool
+HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
+    int size;
+
+    while ((size = ReadInput(ctx)) > 0) {
+        switch (ctx->m_rgucReadBuffer[0]) {
+        case k_eSwitchInputReportIDs_SimpleControllerState:
+            HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
+            break;
+        case k_eSwitchInputReportIDs_FullControllerState:
+            HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (ctx->m_nRumbleExpiration) {
+        Uint32 now = SDL_GetTicks();
+        if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) {
+            HIDAPI_DriverSwitch_Rumble(joystick, dev, context, 0, 0, 0);
+        }
+    }
+
+    return (size >= 0);
+}
+
+static void
+HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
+
+    /* Restore simple input mode for other applications */
+    SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
+
+    SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
+{
+    SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
+    SDL_TRUE,
+    HIDAPI_DriverSwitch_IsSupportedDevice,
+    HIDAPI_DriverSwitch_GetDeviceName,
+    HIDAPI_DriverSwitch_Init,
+    HIDAPI_DriverSwitch_Rumble,
+    HIDAPI_DriverSwitch_Update,
+    HIDAPI_DriverSwitch_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/hidapi/SDL_hidapi_xbox360.c b/source/src/joystick/hidapi/SDL_hidapi_xbox360.c
new file mode 100644
index 0000000..84c63c6
--- /dev/null
+++ b/source/src/joystick/hidapi/SDL_hidapi_xbox360.c
@@ -0,0 +1,787 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
+
+#ifdef __WIN32__
+#define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+/* This requires the Windows 10 SDK to build */
+/*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+#include "../../core/windows/SDL_xinput.h"
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+#include "../../core/windows/SDL_windows.h"
+#define COBJMACROS
+#include "windows.gaming.input.h"
+#endif
+
+#define USB_PACKET_LENGTH   64
+
+
+typedef struct {
+    Uint8 last_state[USB_PACKET_LENGTH];
+    Uint32 rumble_expiration;
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+    SDL_bool xinput_enabled;
+    Uint8 xinput_slot;
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+    SDL_bool coinitialized;
+    __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
+    __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
+    struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
+#endif
+} SDL_DriverXbox360_Context;
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+static Uint8 xinput_slots;
+
+static void
+HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
+{
+    if (xinput_slot != XUSER_INDEX_ANY) {
+        xinput_slots |= (0x01 << xinput_slot);
+    }
+}
+
+static void
+HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
+{
+    if (xinput_slot != XUSER_INDEX_ANY) {
+        xinput_slots &= ~(0x01 << xinput_slot);
+    }
+}
+
+static SDL_bool
+HIDAPI_DriverXbox360_MissingXInputSlot()
+{
+    return xinput_slots != 0x0F;
+}
+
+static Uint8
+HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
+{
+    DWORD user_index;
+    int match_count;
+    Uint8 match_slot;
+
+    if (!XINPUTGETSTATE) {
+        return XUSER_INDEX_ANY;
+    }
+
+    match_count = 0;
+    for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
+        XINPUT_STATE_EX xinput_state;
+
+        if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
+            if (xinput_state.Gamepad.wButtons == wButtons) {
+                ++match_count;
+                match_slot = (Uint8)user_index;
+            }
+        }
+    }
+    if (match_count == 1) {
+        return match_slot;
+    }
+    return XUSER_INDEX_ANY;
+}
+
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+
+static void
+HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
+{
+    /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
+    if (FAILED(WIN_CoInitialize())) {
+        return;
+    }
+    ctx->coinitialized = SDL_TRUE;
+
+    {
+        static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
+        HRESULT hr;
+        HMODULE hModule = LoadLibraryA("combase.dll");
+        if (hModule != NULL) {
+            typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
+            typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
+            typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
+
+            WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
+            WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
+            RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
+            if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
+                LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
+                HSTRING hNamespaceString;
+
+                hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
+                if (SUCCEEDED(hr)) {
+                    RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &ctx->gamepad_statics);
+                    WindowsDeleteStringFunc(hNamespaceString);
+                }
+            }
+            FreeLibrary(hModule);
+        }
+    }
+}
+
+static Uint8
+HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad)
+{
+    HRESULT hr;
+    struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
+    Uint8 buttons = 0;
+
+    hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(gamepad, &state);
+    if (SUCCEEDED(hr)) {
+        if (state.Buttons & GamepadButtons_A) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
+        }
+        if (state.Buttons & GamepadButtons_B) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
+        }
+        if (state.Buttons & GamepadButtons_X) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
+        }
+        if (state.Buttons & GamepadButtons_Y) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
+        }
+    }
+    return buttons;
+}
+
+static void
+HIDAPI_DriverXbox360_GuessGamepad(SDL_DriverXbox360_Context *ctx, Uint8 buttons)
+{
+    HRESULT hr;
+    __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
+
+    hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(ctx->gamepad_statics, &gamepads);
+    if (SUCCEEDED(hr)) {
+        unsigned int i, num_gamepads;
+
+        hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
+        if (SUCCEEDED(hr)) {
+            int match_count;
+            unsigned int match_slot;
+
+            match_count = 0;
+            for (i = 0; i < num_gamepads; ++i) {
+                __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
+
+                hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
+                if (SUCCEEDED(hr)) {
+                    Uint8 gamepad_buttons = HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(gamepad);
+                    if (buttons == gamepad_buttons) {
+                        ++match_count;
+                        match_slot = i;
+                    }
+                    __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
+                }
+            }
+            if (match_count == 1) {
+                hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, match_slot, &ctx->gamepad);
+                if (SUCCEEDED(hr)) {
+                }
+            }
+        }
+        __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
+    }
+}
+
+static void
+HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
+{
+    if (ctx->gamepad_statics) {
+        __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(ctx->gamepad_statics);
+        ctx->gamepad_statics = NULL;
+    }
+    if (ctx->gamepad) {
+        __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(ctx->gamepad);
+        ctx->gamepad = NULL;
+    }
+
+    if (ctx->coinitialized) {
+        WIN_CoUninitialize();
+        ctx->coinitialized = SDL_FALSE;
+    }
+}
+
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
+
+static SDL_bool
+HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
+{
+#if defined(__MACOSX__) || defined(__WIN32__)
+    if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
+        /* This is the Steam Virtual Gamepad, which isn't supported by this driver */
+        return SDL_FALSE;
+    }
+    return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
+#else
+    return SDL_IsJoystickXbox360(vendor_id, product_id);
+#endif
+}
+
+static const char *
+HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+    return HIDAPI_XboxControllerName(vendor_id, product_id);
+}
+
+static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
+{
+    const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) };
+
+    if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
+static SDL_bool
+HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+    SDL_DriverXbox360_Context *ctx;
+
+    ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    }
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+    ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
+    if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
+        ctx->xinput_enabled = SDL_FALSE;
+    }
+    ctx->xinput_slot = XUSER_INDEX_ANY;
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+    HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
+#endif
+    *context = ctx;
+
+    /* Set the controller LED */
+    SetSlotLED(dev, (joystick->instance_id % 4));
+
+    /* Initialize the joystick capabilities */
+    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+    return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
+
+#ifdef __WIN32__
+    SDL_bool rumbled = SDL_FALSE;
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+    if (!rumbled && ctx->gamepad) {
+        HRESULT hr;
+
+        ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
+        ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
+        hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(ctx->gamepad, ctx->vibration);
+        if (SUCCEEDED(hr)) {
+            rumbled = SDL_TRUE;
+        }
+    }
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+    if (!rumbled && ctx->xinput_slot != XUSER_INDEX_ANY) {
+        XINPUT_VIBRATION XVibration;
+
+        if (!XINPUTSETSTATE) {
+            return SDL_Unsupported();
+        }
+
+        XVibration.wLeftMotorSpeed = low_frequency_rumble;
+        XVibration.wRightMotorSpeed = high_frequency_rumble;
+        if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
+            rumbled = SDL_TRUE;
+        } else {
+            return SDL_SetError("XInputSetState() failed");
+        }
+    }
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
+
+#else /* !__WIN32__ */
+
+#ifdef __MACOSX__
+    /* On Mac OS X the 360Controller driver uses this short report,
+       and we need to prefix it with a magic token so hidapi passes it through untouched
+     */
+    Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
+
+    rumble_packet[6+2] = (low_frequency_rumble >> 8);
+    rumble_packet[6+3] = (high_frequency_rumble >> 8);
+#else
+    Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+    rumble_packet[3] = (low_frequency_rumble >> 8);
+    rumble_packet[4] = (high_frequency_rumble >> 8);
+#endif
+
+    if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
+        return SDL_SetError("Couldn't send rumble packet");
+    }
+#endif /* __WIN32__ */
+
+    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+        ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
+    } else {
+        ctx->rumble_expiration = 0;
+    }
+    return 0;
+}
+
+#ifdef __WIN32__
+ /* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
+    however with this interface there is no rumble support, no guide button,
+    and the left and right triggers are tied together as a single axis.
+
+    We use XInput and Windows.Gaming.Input to make up for these shortcomings.
+  */
+static void
+HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
+{
+    Sint16 axis;
+    SDL_bool has_trigger_data = SDL_FALSE;
+
+    if (ctx->last_state[10] != data[10]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[11] != data[11]) {
+        SDL_bool dpad_up = SDL_FALSE;
+        SDL_bool dpad_down = SDL_FALSE;
+        SDL_bool dpad_left = SDL_FALSE;
+        SDL_bool dpad_right = SDL_FALSE;
+
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+
+        switch (data[11] & 0x3C) {
+        case 4:
+            dpad_up = SDL_TRUE;
+            break;
+        case 8:
+            dpad_up = SDL_TRUE;
+            dpad_right = SDL_TRUE;
+            break;
+        case 12:
+            dpad_right = SDL_TRUE;
+            break;
+        case 16:
+            dpad_right = SDL_TRUE;
+            dpad_down = SDL_TRUE;
+            break;
+        case 20:
+            dpad_down = SDL_TRUE;
+            break;
+        case 24:
+            dpad_left = SDL_TRUE;
+            dpad_down = SDL_TRUE;
+            break;
+        case 28:
+            dpad_left = SDL_TRUE;
+            break;
+        case 32:
+            dpad_up = SDL_TRUE;
+            dpad_left = SDL_TRUE;
+            break;
+        default:
+            break;
+        }
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
+    }
+
+    axis = (int)*(Uint16*)(&data[0]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+    axis = (int)*(Uint16*)(&data[2]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+    axis = (int)*(Uint16*)(&data[4]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+    axis = (int)*(Uint16*)(&data[6]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+    if (ctx->gamepad_statics && !ctx->gamepad) {
+        Uint8 buttons = 0;
+
+        if (data[10] & 0x01) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
+        }
+        if (data[10] & 0x02) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
+        }
+        if (data[10] & 0x04) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
+        }
+        if (data[10] & 0x08) {
+            buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
+        }
+        if (buttons != 0) {
+            HIDAPI_DriverXbox360_GuessGamepad(ctx, buttons);
+        }
+    }
+
+    if (ctx->gamepad) {
+        HRESULT hr;
+        struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
+        
+        hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(ctx->gamepad, &state);
+        if (SUCCEEDED(hr)) {
+            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state.Buttons & 0x40000000) ? SDL_PRESSED : SDL_RELEASED);
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state.LeftTrigger * SDL_MAX_UINT16)) - 32768);
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state.RightTrigger * SDL_MAX_UINT16)) - 32768);
+            has_trigger_data = SDL_TRUE;
+        }
+    }
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+    if (ctx->xinput_enabled) {
+        if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
+            WORD wButtons = 0;
+
+            if (data[10] & 0x01) {
+                wButtons |= XINPUT_GAMEPAD_A;
+            }
+            if (data[10] & 0x02) {
+                wButtons |= XINPUT_GAMEPAD_B;
+            }
+            if (data[10] & 0x04) {
+                wButtons |= XINPUT_GAMEPAD_X;
+            }
+            if (data[10] & 0x08) {
+                wButtons |= XINPUT_GAMEPAD_Y;
+            }
+            if (wButtons != 0) {
+                Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
+                if (xinput_slot != XUSER_INDEX_ANY) {
+                    HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
+                    ctx->xinput_slot = xinput_slot;
+                }
+            }
+        }
+
+        if (!has_trigger_data && ctx->xinput_slot != XUSER_INDEX_ANY) {
+            XINPUT_STATE_EX xinput_state;
+
+            if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
+                SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
+                SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
+                SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
+                has_trigger_data = SDL_TRUE;
+            }
+        }
+    }
+#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
+
+    if (!has_trigger_data) {
+        axis = (data[9] * 257) - 32768;
+        if (data[9] < 0x80) {
+            axis = -axis * 2 - 32769;
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+        } else if (data[9] > 0x80) {
+            axis = axis * 2 - 32767;
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+        } else {
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
+            SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
+        }
+    }
+
+    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+#else
+
+static void
+HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
+{
+    Sint16 axis;
+#ifdef __MACOSX__
+    const SDL_bool invert_y_axes = SDL_FALSE;
+#else
+    const SDL_bool invert_y_axes = SDL_TRUE;
+#endif
+
+    if (ctx->last_state[2] != data[2]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[3] != data[3]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    axis = ((int)data[4] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+    axis = ((int)data[5] * 257) - 32768;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+    axis = *(Sint16*)(&data[6]);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+    axis = *(Sint16*)(&data[8]);
+    if (invert_y_axes) {
+        axis = ~axis;
+    }
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+    axis = *(Sint16*)(&data[10]);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+    axis = *(Sint16*)(&data[12]);
+    if (invert_y_axes) {
+        axis = ~axis;
+    }
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+#endif /* __WIN32__ */
+
+#ifdef __MACOSX__
+static void
+HIDAPI_DriverXboxOneS_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
+{
+    Sint16 axis;
+
+    if (ctx->last_state[14] != data[14]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[14] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[14] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[14] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[14] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[14] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[14] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[15] != data[15]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[15] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[15] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[15] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[16] != data[16]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[13] != data[13]) {
+        SDL_bool dpad_up = SDL_FALSE;
+        SDL_bool dpad_down = SDL_FALSE;
+        SDL_bool dpad_left = SDL_FALSE;
+        SDL_bool dpad_right = SDL_FALSE;
+
+        switch (data[13]) {
+        case 1:
+            dpad_up = SDL_TRUE;
+            break;
+        case 2:
+            dpad_up = SDL_TRUE;
+            dpad_right = SDL_TRUE;
+            break;
+        case 3:
+            dpad_right = SDL_TRUE;
+            break;
+        case 4:
+            dpad_right = SDL_TRUE;
+            dpad_down = SDL_TRUE;
+            break;
+        case 5:
+            dpad_down = SDL_TRUE;
+            break;
+        case 6:
+            dpad_left = SDL_TRUE;
+            dpad_down = SDL_TRUE;
+            break;
+        case 7:
+            dpad_left = SDL_TRUE;
+            break;
+        case 8:
+            dpad_up = SDL_TRUE;
+            dpad_left = SDL_TRUE;
+            break;
+        default:
+            break;
+        }
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
+    }
+
+    axis = (int)*(Uint16*)(&data[1]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+    axis = (int)*(Uint16*)(&data[3]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+    axis = (int)*(Uint16*)(&data[5]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+    axis = (int)*(Uint16*)(&data[7]) - 0x8000;
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+    axis = ((int)*(Sint16*)(&data[9]) * 64) - 32768;
+    if (axis == 32704) {
+        axis = 32767;
+    }
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+
+    axis = ((int)*(Sint16*)(&data[11]) * 64) - 32768;
+    if (axis == 32704) {
+        axis = 32767;
+    }
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+
+    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+
+static void
+HIDAPI_DriverXboxOneS_HandleGuidePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
+{
+    SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[1] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+}
+#endif /* __MACOSX__ */
+
+static SDL_bool
+HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
+    Uint8 data[USB_PACKET_LENGTH];
+    int size;
+
+    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
+#ifdef __WIN32__
+        HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
+#else
+        switch (data[0]) {
+        case 0x00:
+            HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
+            break;
+#ifdef __MACOSX__
+        case 0x01:
+            HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, dev, ctx, data, size);
+            break;
+        case 0x02:
+            HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, dev, ctx, data, size);
+            break;
+#endif
+        default:
+#ifdef DEBUG_JOYSTICK
+            SDL_Log("Unknown Xbox 360 packet, size = %d\n", size);
+            SDL_Log("%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
+                data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
+                data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16]);
+#endif
+            break;
+        }
+#endif /* __WIN32__ */
+    }
+
+    if (ctx->rumble_expiration) {
+        Uint32 now = SDL_GetTicks();
+        if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
+            HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0);
+        }
+    }
+
+    return (size >= 0);
+}
+
+static void
+HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
+    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
+#endif
+
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
+    if (ctx->xinput_enabled) {
+        HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
+        WIN_UnloadXInputDLL();
+    }
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
+    HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
+#endif
+    SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
+{
+    SDL_HINT_JOYSTICK_HIDAPI_XBOX,
+    SDL_TRUE,
+    HIDAPI_DriverXbox360_IsSupportedDevice,
+    HIDAPI_DriverXbox360_GetDeviceName,
+    HIDAPI_DriverXbox360_Init,
+    HIDAPI_DriverXbox360_Rumble,
+    HIDAPI_DriverXbox360_Update,
+    HIDAPI_DriverXbox360_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/hidapi/SDL_hidapi_xboxone.c b/source/src/joystick/hidapi/SDL_hidapi_xboxone.c
new file mode 100644
index 0000000..2cd593f
--- /dev/null
+++ b/source/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -0,0 +1,324 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
+
+#define USB_PACKET_LENGTH   64
+
+/*
+ * This packet is required for all Xbox One pads with 2015
+ * or later firmware installed (or present from the factory).
+ */
+static const Uint8 xboxone_fw2015_init[] = {
+    0x05, 0x20, 0x00, 0x01, 0x00
+};
+
+/*
+ * This packet is required for the Titanfall 2 Xbox One pads
+ * (0x0e6f:0x0165) to finish initialization and for Hori pads
+ * (0x0f0d:0x0067) to make the analog sticks work.
+ */
+static const Uint8 xboxone_hori_init[] = {
+    0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
+    0x00, 0x00, 0x00, 0x80, 0x00
+};
+
+/*
+ * This packet is required for some of the PDP pads to start
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4).
+ */
+static const Uint8 xboxone_pdp_init1[] = {
+    0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
+};
+
+/*
+ * This packet is required for some of the PDP pads to start
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4).
+ */
+static const Uint8 xboxone_pdp_init2[] = {
+    0x06, 0x20, 0x00, 0x02, 0x01, 0x00
+};
+
+/*
+ * A specific rumble packet is required for some PowerA pads to start
+ * sending input reports. One of those pads is (0x24c6:0x543a).
+ */
+static const Uint8 xboxone_rumblebegin_init[] = {
+    0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+    0x1D, 0x1D, 0xFF, 0x00, 0x00
+};
+
+/*
+ * A rumble packet with zero FF intensity will immediately
+ * terminate the rumbling required to init PowerA pads.
+ * This should happen fast enough that the motors don't
+ * spin up to enough speed to actually vibrate the gamepad.
+ */
+static const Uint8 xboxone_rumbleend_init[] = {
+    0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * This specifies the selection of init packets that a gamepad
+ * will be sent on init *and* the order in which they will be
+ * sent. The correct sequence number will be added when the
+ * packet is going to be sent.
+ */
+typedef struct {
+    Uint16 vendor_id;
+    Uint16 product_id;
+    const Uint8 *data;
+    int size;
+} SDL_DriverXboxOne_InitPacket;
+
+static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
+    { 0x0e6f, 0x0165, xboxone_hori_init, sizeof(xboxone_hori_init) },
+    { 0x0f0d, 0x0067, xboxone_hori_init, sizeof(xboxone_hori_init) },
+    { 0x0000, 0x0000, xboxone_fw2015_init, sizeof(xboxone_fw2015_init) },
+    { 0x0e6f, 0x0246, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
+    { 0x0e6f, 0x0246, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
+    { 0x0e6f, 0x02ab, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
+    { 0x0e6f, 0x02ab, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
+    { 0x0e6f, 0x02a4, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
+    { 0x0e6f, 0x02a4, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
+    { 0x24c6, 0x541a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
+    { 0x24c6, 0x542a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
+    { 0x24c6, 0x543a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
+    { 0x24c6, 0x541a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
+    { 0x24c6, 0x542a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
+    { 0x24c6, 0x543a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
+};
+
+typedef struct {
+    Uint8 sequence;
+    Uint8 last_state[USB_PACKET_LENGTH];
+    Uint32 rumble_expiration;
+} SDL_DriverXboxOne_Context;
+
+
+static SDL_bool
+HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
+{
+    return SDL_IsJoystickXboxOne(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+    return HIDAPI_XboxControllerName(vendor_id, product_id);
+}
+
+static SDL_bool
+HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+    SDL_DriverXboxOne_Context *ctx;
+    int i;
+    Uint8 init_packet[USB_PACKET_LENGTH];
+
+    ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    }
+    *context = ctx;
+
+    /* Send the controller init data */
+    for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
+        const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
+        if (!packet->vendor_id || (vendor_id == packet->vendor_id && product_id == packet->product_id)) {
+            SDL_memcpy(init_packet, packet->data, packet->size);
+            init_packet[2] = ctx->sequence++;
+            if (hid_write(dev, init_packet, packet->size) != packet->size) {
+                SDL_SetError("Couldn't write Xbox One initialization packet");
+                SDL_free(ctx);
+                return SDL_FALSE;
+            }
+        }
+    }
+
+    /* Initialize the joystick capabilities */
+    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+    return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
+    Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
+
+    /* The Rock Candy Xbox One Controller limits the range of
+         low frequency rumble strength in the range of [0 - 0x99]
+         high frequency rumble strength in the range of [0 - 0x82]
+
+       I think the valid range of rumble at the firmware level is [0 - 0x7F]
+    */
+    rumble_packet[2] = ctx->sequence++;
+    rumble_packet[8] = (low_frequency_rumble >> 9);
+    rumble_packet[9] = (high_frequency_rumble >> 9);
+
+    if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
+        return SDL_SetError("Couldn't send rumble packet");
+    }
+
+    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+        ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
+    } else {
+        ctx->rumble_expiration = 0;
+    }
+    return 0;
+}
+
+static void
+HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
+{
+    Sint16 axis;
+
+    if (ctx->last_state[4] != data[4]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    if (ctx->last_state[5] != data[5]) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+    }
+
+    axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
+    if (axis == 32704) {
+        axis = 32767;
+    }
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+    axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
+    if (axis == 32704) {
+        axis = 32767;
+    }
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+    axis = *(Sint16*)(&data[10]);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+    axis = *(Sint16*)(&data[12]);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
+    axis = *(Sint16*)(&data[14]);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+    axis = *(Sint16*)(&data[16]);
+    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
+
+    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+
+static void
+HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
+{
+    if (data[1] == 0x30) {
+        /* The Xbox One S controller needs acks for mode reports */
+        const Uint8 seqnum = data[2];
+        const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
+        hid_write(dev, ack, sizeof(ack));
+    }
+
+    SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+}
+
+static SDL_bool
+HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
+    Uint8 data[USB_PACKET_LENGTH];
+    int size;
+
+    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
+        switch (data[0]) {
+        case 0x20:
+            HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
+            break;
+        case 0x07:
+            HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
+            break;
+        default:
+#ifdef DEBUG_JOYSTICK
+            SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
+#endif
+            break;
+        }
+    }
+
+    if (ctx->rumble_expiration) {
+        Uint32 now = SDL_GetTicks();
+        if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
+            HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
+        }
+    }
+
+    return (size >= 0);
+}
+
+static void
+HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+    SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
+{
+    SDL_HINT_JOYSTICK_HIDAPI_XBOX,
+    SDL_TRUE,
+    HIDAPI_DriverXboxOne_IsSupportedDevice,
+    HIDAPI_DriverXboxOne_GetDeviceName,
+    HIDAPI_DriverXboxOne_Init,
+    HIDAPI_DriverXboxOne_Rumble,
+    HIDAPI_DriverXboxOne_Update,
+    HIDAPI_DriverXboxOne_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/hidapi/SDL_hidapijoystick.c b/source/src/joystick/hidapi/SDL_hidapijoystick.c
new file mode 100644
index 0000000..064cb82
--- /dev/null
+++ b/source/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -0,0 +1,1071 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_endian.h"
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_mutex.h"
+#include "SDL_thread.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+#if defined(__WIN32__)
+#include "../../core/windows/SDL_windows.h"
+#endif
+
+#if defined(__MACOSX__)
+#include <CoreFoundation/CoreFoundation.h>
+#include <mach/mach.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/USBSpec.h>
+#endif
+
+#if defined(__LINUX__)
+#include "../../core/linux/SDL_udev.h"
+#ifdef SDL_USE_LIBUDEV
+#include <poll.h>
+#endif
+#endif
+
+struct joystick_hwdata
+{
+    SDL_HIDAPI_DeviceDriver *driver;
+    void *context;
+
+    SDL_mutex *mutex;
+    hid_device *dev;
+};
+
+typedef struct _SDL_HIDAPI_Device
+{
+    SDL_JoystickID instance_id;
+    char *name;
+    char *path;
+    Uint16 vendor_id;
+    Uint16 product_id;
+    Uint16 version;
+    SDL_JoystickGUID guid;
+    int interface_number;   /* Available on Windows and Linux */
+    Uint16 usage_page;      /* Available on Windows and Mac OS X */
+    Uint16 usage;           /* Available on Windows and Mac OS X */
+    SDL_HIDAPI_DeviceDriver *driver;
+
+    /* Used during scanning for device changes */
+    SDL_bool seen;
+
+    struct _SDL_HIDAPI_Device *next;
+} SDL_HIDAPI_Device;
+
+static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
+#ifdef SDL_JOYSTICK_HIDAPI_PS4
+    &SDL_HIDAPI_DriverPS4,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_STEAM
+    &SDL_HIDAPI_DriverSteam,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
+    &SDL_HIDAPI_DriverSwitch,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
+    &SDL_HIDAPI_DriverXbox360,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
+    &SDL_HIDAPI_DriverXboxOne,
+#endif
+};
+static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
+static int SDL_HIDAPI_numjoysticks = 0;
+
+#if defined(SDL_USE_LIBUDEV)
+static const SDL_UDEV_Symbols * usyms = NULL;
+#endif
+
+static struct
+{
+    SDL_bool m_bHaveDevicesChanged;
+    SDL_bool m_bCanGetNotifications;
+    Uint32 m_unLastDetect;
+
+#if defined(__WIN32__)
+    SDL_threadID m_nThreadID;
+    WNDCLASSEXA m_wndClass;
+    HWND m_hwndMsg;
+    HDEVNOTIFY m_hNotify;
+    double m_flLastWin32MessageCheck;
+#endif
+
+#if defined(__MACOSX__)
+    IONotificationPortRef m_notificationPort;
+    mach_port_t m_notificationMach;
+#endif
+
+#if defined(SDL_USE_LIBUDEV)
+    struct udev *m_pUdev;
+    struct udev_monitor *m_pUdevMonitor;
+    int m_nUdevFd;
+#endif
+} SDL_HIDAPI_discovery;
+
+
+#ifdef __WIN32__
+struct _DEV_BROADCAST_HDR
+{
+    DWORD       dbch_size;
+    DWORD       dbch_devicetype;
+    DWORD       dbch_reserved;
+};
+
+typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
+{
+    DWORD       dbcc_size;
+    DWORD       dbcc_devicetype;
+    DWORD       dbcc_reserved;
+    GUID        dbcc_classguid;
+    char        dbcc_name[ 1 ];
+} DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
+
+typedef struct  _DEV_BROADCAST_HDR      DEV_BROADCAST_HDR;
+#define DBT_DEVICEARRIVAL               0x8000  /* system detected a new device */
+#define DBT_DEVICEREMOVECOMPLETE        0x8004  /* device was removed from the system */
+#define DBT_DEVTYP_DEVICEINTERFACE      0x00000005  /* device interface class */
+#define DBT_DEVNODES_CHANGED            0x0007
+#define DBT_CONFIGCHANGED               0x0018
+#define DBT_DEVICETYPESPECIFIC          0x8005  /* type specific event */
+#define DBT_DEVINSTSTARTED              0x8008  /* device installed and started */
+
+#include <initguid.h>
+DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
+
+static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    switch (message) {
+    case WM_DEVICECHANGE:
+        switch (wParam) {
+        case DBT_DEVICEARRIVAL:
+        case DBT_DEVICEREMOVECOMPLETE:
+            if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
+                SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
+            }
+            break;
+        }
+        return TRUE;
+    }
+
+    return DefWindowProc(hwnd, message, wParam, lParam);
+}
+#endif /* __WIN32__ */
+
+
+#if defined(__MACOSX__)
+static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
+{
+    /* Must drain the iterator, or we won't receive new notifications */
+    io_object_t entry;
+    while ((entry = IOIteratorNext(portIterator)) != 0) {
+        IOObjectRelease(entry);
+        *(SDL_bool*)context = SDL_TRUE;
+    }
+}
+#endif /* __MACOSX__ */
+
+static void
+HIDAPI_InitializeDiscovery()
+{
+    SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
+    SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_FALSE;
+    SDL_HIDAPI_discovery.m_unLastDetect = 0;
+
+#if defined(__WIN32__)
+    SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
+
+    SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
+    SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
+    SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
+    SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc;      /* This function is called by windows */
+    SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
+
+    RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
+    SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
+
+    {
+        DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
+        SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
+
+        devBroadcast.dbcc_size = sizeof( devBroadcast );
+        devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+        devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
+
+        /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
+         * but that seems to be necessary to get a notice after each individual usb input device actually
+         * installs, rather than just as the composite device is seen.
+         */
+        SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification( SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
+        SDL_HIDAPI_discovery.m_bCanGetNotifications = ( SDL_HIDAPI_discovery.m_hNotify != 0 );
+    }
+#endif /* __WIN32__ */
+
+#if defined(__MACOSX__)
+    SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+    if (SDL_HIDAPI_discovery.m_notificationPort) {
+        {
+            CFMutableDictionaryRef matchingDict = IOServiceMatching("IOUSBDevice");
+
+            /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
+            io_iterator_t portIterator = 0;
+            io_object_t entry;
+            if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
+                /* Must drain the existing iterator, or we won't receive new notifications */
+                while ((entry = IOIteratorNext(portIterator)) != 0) {
+                    IOObjectRelease(entry);
+                }
+            } else {
+                IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
+                SDL_HIDAPI_discovery.m_notificationPort = nil;
+            }
+        }
+        {
+            CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBluetoothDevice");
+
+            /* Note: IOServiceAddMatchingNotification consumes the reference to matchingDict */
+            io_iterator_t portIterator = 0;
+            io_object_t entry;
+            if (IOServiceAddMatchingNotification(SDL_HIDAPI_discovery.m_notificationPort, kIOMatchedNotification, matchingDict, CallbackIOServiceFunc, &SDL_HIDAPI_discovery.m_bHaveDevicesChanged, &portIterator) == 0) {
+                /* Must drain the existing iterator, or we won't receive new notifications */
+                while ((entry = IOIteratorNext(portIterator)) != 0) {
+                    IOObjectRelease(entry);
+                }
+            } else {
+                IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
+                SDL_HIDAPI_discovery.m_notificationPort = nil;
+            }
+        }
+    }
+
+    SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
+    if (SDL_HIDAPI_discovery.m_notificationPort) {
+        SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
+    }
+
+    SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
+
+#endif // __MACOSX__
+
+#if defined(SDL_USE_LIBUDEV)
+    SDL_HIDAPI_discovery.m_pUdev = NULL;
+    SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
+    SDL_HIDAPI_discovery.m_nUdevFd = -1;
+
+    usyms = SDL_UDEV_GetUdevSyms();
+    if (usyms) {
+        SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
+    }
+    if (SDL_HIDAPI_discovery.m_pUdev) {
+        SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
+        if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
+            usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
+            SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
+            SDL_HIDAPI_discovery.m_bCanGetNotifications = SDL_TRUE;
+        }
+    }
+
+#endif /* SDL_USE_LIBUDEV */
+}
+
+static void
+HIDAPI_UpdateDiscovery()
+{
+    if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
+        const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000;  /* Update every 3 seconds */
+        Uint32 now = SDL_GetTicks();
+        if (!SDL_HIDAPI_discovery.m_unLastDetect || SDL_TICKS_PASSED(now, SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
+            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
+            SDL_HIDAPI_discovery.m_unLastDetect = now;
+        }
+        return;
+    }
+
+#if defined(__WIN32__)
+#if 0 /* just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan. */
+    /* We'll only get messages on the same thread that created the window */
+    if (SDL_ThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
+        MSG msg;
+        while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
+            if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
+                TranslateMessage(&msg);
+                DispatchMessage(&msg);
+            }
+        }
+    }
+#endif
+#endif /* __WIN32__ */
+
+#if defined(__MACOSX__)
+    if (SDL_HIDAPI_discovery.m_notificationPort) {
+        struct { mach_msg_header_t hdr; char payload[ 4096 ]; } msg;
+        while (mach_msg(&msg.hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), SDL_HIDAPI_discovery.m_notificationMach, 0, MACH_PORT_NULL) == KERN_SUCCESS) {
+            IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
+        }
+    }
+#endif
+
+#if defined(SDL_USE_LIBUDEV)
+    if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
+        /* Drain all notification events.
+         * We don't expect a lot of device notifications so just
+         * do a new discovery on any kind or number of notifications.
+         * This could be made more restrictive if necessary.
+         */
+        for (;;) {
+            struct pollfd PollUdev;
+            struct udev_device *pUdevDevice;
+
+            PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
+            PollUdev.events = POLLIN;
+            if (poll(&PollUdev, 1, 0) != 1) {
+                break;
+            }
+
+            SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_TRUE;
+
+            pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
+            if (pUdevDevice) {
+                usyms->udev_device_unref(pUdevDevice);
+            }
+        }
+    }
+#endif
+}
+
+static void
+HIDAPI_ShutdownDiscovery()
+{
+#if defined(__WIN32__)
+    if (SDL_HIDAPI_discovery.m_hNotify)
+        UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
+
+    if (SDL_HIDAPI_discovery.m_hwndMsg) {
+        DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
+    }
+
+    UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
+#endif
+
+#if defined(__MACOSX__)
+    if (SDL_HIDAPI_discovery.m_notificationPort) {
+        IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
+    }
+#endif
+
+#if defined(SDL_USE_LIBUDEV)
+    if (usyms) {
+        if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
+            usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
+        }
+        if (SDL_HIDAPI_discovery.m_pUdev) {
+            usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
+        }
+        SDL_UDEV_ReleaseUdevSyms();
+        usyms = NULL;
+    }
+#endif
+}
+
+
+const char *
+HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
+{
+    static struct
+    {
+        Uint32 vidpid;
+        const char *name;
+    } names[] = {
+        { MAKE_VIDPID(0x0079, 0x18d4), "GPD Win 2 X-Box Controller" },
+        { MAKE_VIDPID(0x044f, 0xb326), "Thrustmaster Gamepad GP XID" },
+        { MAKE_VIDPID(0x045e, 0x028e), "Microsoft X-Box 360 pad" },
+        { MAKE_VIDPID(0x045e, 0x028f), "Microsoft X-Box 360 pad v2" },
+        { MAKE_VIDPID(0x045e, 0x0291), "Xbox 360 Wireless Receiver (XBOX)" },
+        { MAKE_VIDPID(0x045e, 0x02d1), "Microsoft X-Box One pad" },
+        { MAKE_VIDPID(0x045e, 0x02dd), "Microsoft X-Box One pad (Firmware 2015)" },
+        { MAKE_VIDPID(0x045e, 0x02e3), "Microsoft X-Box One Elite pad" },
+        { MAKE_VIDPID(0x045e, 0x02ea), "Microsoft X-Box One S pad" },
+        { MAKE_VIDPID(0x045e, 0x02ff), "Microsoft X-Box One pad" },
+        { MAKE_VIDPID(0x045e, 0x0719), "Xbox 360 Wireless Receiver" },
+        { MAKE_VIDPID(0x046d, 0xc21d), "Logitech Gamepad F310" },
+        { MAKE_VIDPID(0x046d, 0xc21e), "Logitech Gamepad F510" },
+        { MAKE_VIDPID(0x046d, 0xc21f), "Logitech Gamepad F710" },
+        { MAKE_VIDPID(0x046d, 0xc242), "Logitech Chillstream Controller" },
+        { MAKE_VIDPID(0x046d, 0xcaa3), "Logitech DriveFx Racing Wheel" },
+        { MAKE_VIDPID(0x056e, 0x2004), "Elecom JC-U3613M" },
+        { MAKE_VIDPID(0x06a3, 0xf51a), "Saitek P3600" },
+        { MAKE_VIDPID(0x0738, 0x4716), "Mad Catz Wired Xbox 360 Controller" },
+        { MAKE_VIDPID(0x0738, 0x4718), "Mad Catz Street Fighter IV FightStick SE" },
+        { MAKE_VIDPID(0x0738, 0x4726), "Mad Catz Xbox 360 Controller" },
+        { MAKE_VIDPID(0x0738, 0x4728), "Mad Catz Street Fighter IV FightPad" },
+        { MAKE_VIDPID(0x0738, 0x4736), "Mad Catz MicroCon Gamepad" },
+        { MAKE_VIDPID(0x0738, 0x4738), "Mad Catz Wired Xbox 360 Controller (SFIV)" },
+        { MAKE_VIDPID(0x0738, 0x4740), "Mad Catz Beat Pad" },
+        { MAKE_VIDPID(0x0738, 0x4758), "Mad Catz Arcade Game Stick" },
+        { MAKE_VIDPID(0x0738, 0x4a01), "Mad Catz FightStick TE 2" },
+        { MAKE_VIDPID(0x0738, 0x9871), "Mad Catz Portable Drum" },
+        { MAKE_VIDPID(0x0738, 0xb726), "Mad Catz Xbox controller - MW2" },
+        { MAKE_VIDPID(0x0738, 0xb738), "Mad Catz MVC2TE Stick 2" },
+        { MAKE_VIDPID(0x0738, 0xbeef), "Mad Catz JOYTECH NEO SE Advanced GamePad" },
+        { MAKE_VIDPID(0x0738, 0xcb02), "Saitek Cyborg Rumble Pad - PC/Xbox 360" },
+        { MAKE_VIDPID(0x0738, 0xcb03), "Saitek P3200 Rumble Pad - PC/Xbox 360" },
+        { MAKE_VIDPID(0x0738, 0xcb29), "Saitek Aviator Stick AV8R02" },
+        { MAKE_VIDPID(0x0738, 0xf738), "Super SFIV FightStick TE S" },
+        { MAKE_VIDPID(0x07ff, 0xffff), "Mad Catz GamePad" },
+        { MAKE_VIDPID(0x0e6f, 0x0105), "HSM3 Xbox360 dancepad" },
+        { MAKE_VIDPID(0x0e6f, 0x0113), "Afterglow AX.1 Gamepad for Xbox 360" },
+        { MAKE_VIDPID(0x0e6f, 0x011f), "Rock Candy Gamepad Wired Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0131), "PDP EA Sports Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0133), "Xbox 360 Wired Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0139), "Afterglow Prismatic Wired Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x013a), "PDP Xbox One Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0146), "Rock Candy Wired Controller for Xbox One" },
+        { MAKE_VIDPID(0x0e6f, 0x0147), "PDP Marvel Xbox One Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x015c), "PDP Xbox One Arcade Stick" },
+        { MAKE_VIDPID(0x0e6f, 0x0161), "PDP Xbox One Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0162), "PDP Xbox One Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0163), "PDP Xbox One Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0164), "PDP Battlefield One" },
+        { MAKE_VIDPID(0x0e6f, 0x0165), "PDP Titanfall 2" },
+        { MAKE_VIDPID(0x0e6f, 0x0201), "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0213), "Afterglow Gamepad for Xbox 360" },
+        { MAKE_VIDPID(0x0e6f, 0x021f), "Rock Candy Gamepad for Xbox 360" },
+        { MAKE_VIDPID(0x0e6f, 0x0246), "Rock Candy Gamepad for Xbox One 2015" },
+        { MAKE_VIDPID(0x0e6f, 0x02a4), "PDP Wired Controller for Xbox One - Stealth Series" },
+        { MAKE_VIDPID(0x0e6f, 0x02ab), "PDP Controller for Xbox One" },
+        { MAKE_VIDPID(0x0e6f, 0x0301), "Logic3 Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0346), "Rock Candy Gamepad for Xbox One 2016" },
+        { MAKE_VIDPID(0x0e6f, 0x0401), "Logic3 Controller" },
+        { MAKE_VIDPID(0x0e6f, 0x0413), "Afterglow AX.1 Gamepad for Xbox 360" },
+        { MAKE_VIDPID(0x0e6f, 0x0501), "PDP Xbox 360 Controller" },
+        { MAKE_VIDPID(0x0e6f, 0xf900), "PDP Afterglow AX.1" },
+        { MAKE_VIDPID(0x0f0d, 0x000a), "Hori Co. DOA4 FightStick" },
+        { MAKE_VIDPID(0x0f0d, 0x000c), "Hori PadEX Turbo" },
+        { MAKE_VIDPID(0x0f0d, 0x000d), "Hori Fighting Stick EX2" },
+        { MAKE_VIDPID(0x0f0d, 0x0016), "Hori Real Arcade Pro.EX" },
+        { MAKE_VIDPID(0x0f0d, 0x001b), "Hori Real Arcade Pro VX" },
+        { MAKE_VIDPID(0x0f0d, 0x0063), "Hori Real Arcade Pro Hayabusa (USA) Xbox One" },
+        { MAKE_VIDPID(0x0f0d, 0x0067), "HORIPAD ONE" },
+        { MAKE_VIDPID(0x0f0d, 0x0078), "Hori Real Arcade Pro V Kai Xbox One" },
+        { MAKE_VIDPID(0x11c9, 0x55f0), "Nacon GC-100XF" },
+        { MAKE_VIDPID(0x12ab, 0x0004), "Honey Bee Xbox360 dancepad" },
+        { MAKE_VIDPID(0x12ab, 0x0301), "PDP AFTERGLOW AX.1" },
+        { MAKE_VIDPID(0x12ab, 0x0303), "Mortal Kombat Klassic FightStick" },
+        { MAKE_VIDPID(0x1430, 0x4748), "RedOctane Guitar Hero X-plorer" },
+        { MAKE_VIDPID(0x1430, 0xf801), "RedOctane Controller" },
+        { MAKE_VIDPID(0x146b, 0x0601), "BigBen Interactive XBOX 360 Controller" },
+        { MAKE_VIDPID(0x1532, 0x0037), "Razer Sabertooth" },
+        { MAKE_VIDPID(0x1532, 0x0a00), "Razer Atrox Arcade Stick" },
+        { MAKE_VIDPID(0x1532, 0x0a03), "Razer Wildcat" },
+        { MAKE_VIDPID(0x15e4, 0x3f00), "Power A Mini Pro Elite" },
+        { MAKE_VIDPID(0x15e4, 0x3f0a), "Xbox Airflo wired controller" },
+        { MAKE_VIDPID(0x15e4, 0x3f10), "Batarang Xbox 360 controller" },
+        { MAKE_VIDPID(0x162e, 0xbeef), "Joytech Neo-Se Take2" },
+        { MAKE_VIDPID(0x1689, 0xfd00), "Razer Onza Tournament Edition" },
+        { MAKE_VIDPID(0x1689, 0xfd01), "Razer Onza Classic Edition" },
+        { MAKE_VIDPID(0x1689, 0xfe00), "Razer Sabertooth" },
+        { MAKE_VIDPID(0x1bad, 0x0002), "Harmonix Rock Band Guitar" },
+        { MAKE_VIDPID(0x1bad, 0x0003), "Harmonix Rock Band Drumkit" },
+        { MAKE_VIDPID(0x1bad, 0x0130), "Ion Drum Rocker" },
+        { MAKE_VIDPID(0x1bad, 0xf016), "Mad Catz Xbox 360 Controller" },
+        { MAKE_VIDPID(0x1bad, 0xf018), "Mad Catz Street Fighter IV SE Fighting Stick" },
+        { MAKE_VIDPID(0x1bad, 0xf019), "Mad Catz Brawlstick for Xbox 360" },
+        { MAKE_VIDPID(0x1bad, 0xf021), "Mad Cats Ghost Recon FS GamePad" },
+        { MAKE_VIDPID(0x1bad, 0xf023), "MLG Pro Circuit Controller (Xbox)" },
+        { MAKE_VIDPID(0x1bad, 0xf025), "Mad Catz Call Of Duty" },
+        { MAKE_VIDPID(0x1bad, 0xf027), "Mad Catz FPS Pro" },
+        { MAKE_VIDPID(0x1bad, 0xf028), "Street Fighter IV FightPad" },
+        { MAKE_VIDPID(0x1bad, 0xf02e), "Mad Catz Fightpad" },
+        { MAKE_VIDPID(0x1bad, 0xf030), "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel" },
+        { MAKE_VIDPID(0x1bad, 0xf036), "Mad Catz MicroCon GamePad Pro" },
+        { MAKE_VIDPID(0x1bad, 0xf038), "Street Fighter IV FightStick TE" },
+        { MAKE_VIDPID(0x1bad, 0xf039), "Mad Catz MvC2 TE" },
+        { MAKE_VIDPID(0x1bad, 0xf03a), "Mad Catz SFxT Fightstick Pro" },
+        { MAKE_VIDPID(0x1bad, 0xf03d), "Street Fighter IV Arcade Stick TE - Chun Li" },
+        { MAKE_VIDPID(0x1bad, 0xf03e), "Mad Catz MLG FightStick TE" },
+        { MAKE_VIDPID(0x1bad, 0xf03f), "Mad Catz FightStick SoulCaliber" },
+        { MAKE_VIDPID(0x1bad, 0xf042), "Mad Catz FightStick TES+" },
+        { MAKE_VIDPID(0x1bad, 0xf080), "Mad Catz FightStick TE2" },
+        { MAKE_VIDPID(0x1bad, 0xf501), "HoriPad EX2 Turbo" },
+        { MAKE_VIDPID(0x1bad, 0xf502), "Hori Real Arcade Pro.VX SA" },
+        { MAKE_VIDPID(0x1bad, 0xf503), "Hori Fighting Stick VX" },
+        { MAKE_VIDPID(0x1bad, 0xf504), "Hori Real Arcade Pro. EX" },
+        { MAKE_VIDPID(0x1bad, 0xf505), "Hori Fighting Stick EX2B" },
+        { MAKE_VIDPID(0x1bad, 0xf506), "Hori Real Arcade Pro.EX Premium VLX" },
+        { MAKE_VIDPID(0x1bad, 0xf900), "Harmonix Xbox 360 Controller" },
+        { MAKE_VIDPID(0x1bad, 0xf901), "Gamestop Xbox 360 Controller" },
+        { MAKE_VIDPID(0x1bad, 0xf903), "Tron Xbox 360 controller" },
+        { MAKE_VIDPID(0x1bad, 0xf904), "PDP Versus Fighting Pad" },
+        { MAKE_VIDPID(0x1bad, 0xf906), "MortalKombat FightStick" },
+        { MAKE_VIDPID(0x1bad, 0xfa01), "MadCatz GamePad" },
+        { MAKE_VIDPID(0x1bad, 0xfd00), "Razer Onza TE" },
+        { MAKE_VIDPID(0x1bad, 0xfd01), "Razer Onza" },
+        { MAKE_VIDPID(0x24c6, 0x5000), "Razer Atrox Arcade Stick" },
+        { MAKE_VIDPID(0x24c6, 0x5300), "PowerA MINI PROEX Controller" },
+        { MAKE_VIDPID(0x24c6, 0x5303), "Xbox Airflo wired controller" },
+        { MAKE_VIDPID(0x24c6, 0x530a), "Xbox 360 Pro EX Controller" },
+        { MAKE_VIDPID(0x24c6, 0x531a), "PowerA Pro Ex" },
+        { MAKE_VIDPID(0x24c6, 0x5397), "FUS1ON Tournament Controller" },
+        { MAKE_VIDPID(0x24c6, 0x541a), "PowerA Xbox One Mini Wired Controller" },
+        { MAKE_VIDPID(0x24c6, 0x542a), "Xbox ONE spectra" },
+        { MAKE_VIDPID(0x24c6, 0x543a), "PowerA Xbox One wired controller" },
+        { MAKE_VIDPID(0x24c6, 0x5500), "Hori XBOX 360 EX 2 with Turbo" },
+        { MAKE_VIDPID(0x24c6, 0x5501), "Hori Real Arcade Pro VX-SA" },
+        { MAKE_VIDPID(0x24c6, 0x5502), "Hori Fighting Stick VX Alt" },
+        { MAKE_VIDPID(0x24c6, 0x5503), "Hori Fighting Edge" },
+        { MAKE_VIDPID(0x24c6, 0x5506), "Hori SOULCALIBUR V Stick" },
+        { MAKE_VIDPID(0x24c6, 0x550d), "Hori GEM Xbox controller" },
+        { MAKE_VIDPID(0x24c6, 0x550e), "Hori Real Arcade Pro V Kai 360" },
+        { MAKE_VIDPID(0x24c6, 0x551a), "PowerA FUSION Pro Controller" },
+        { MAKE_VIDPID(0x24c6, 0x561a), "PowerA FUSION Controller" },
+        { MAKE_VIDPID(0x24c6, 0x5b00), "ThrustMaster Ferrari 458 Racing Wheel" },
+        { MAKE_VIDPID(0x24c6, 0x5b02), "Thrustmaster, Inc. GPX Controller" },
+        { MAKE_VIDPID(0x24c6, 0x5b03), "Thrustmaster Ferrari 458 Racing Wheel" },
+        { MAKE_VIDPID(0x24c6, 0x5d04), "Razer Sabertooth" },
+        { MAKE_VIDPID(0x24c6, 0xfafe), "Rock Candy Gamepad for Xbox 360" },
+    };
+    int i;
+    Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
+
+    for (i = 0; i < SDL_arraysize(names); ++i) {
+        if (vidpid == names[i].vidpid) {
+            return names[i].name;
+        }
+    }
+    return NULL;
+}
+
+static SDL_bool
+HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version)
+{
+    int i;
+
+    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+        if (driver->enabled && driver->IsSupportedDevice(vendor_id, product_id, version, -1)) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static SDL_HIDAPI_DeviceDriver *
+HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
+{
+    const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;
+    const Uint16 USAGE_JOYSTICK = 0x0004;
+    const Uint16 USAGE_GAMEPAD = 0x0005;
+    const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
+    int i;
+
+    if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
+        return NULL;
+    }
+
+    if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
+        return NULL;
+    }
+    if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
+        return NULL;
+    }
+
+    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+        if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->version, device->interface_number)) {
+            return driver;
+        }
+    }
+    return NULL;
+}
+
+static SDL_HIDAPI_Device *
+HIDAPI_GetJoystickByIndex(int device_index)
+{
+    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+    while (device) {
+        if (device->driver) {
+            if (device_index == 0) {
+                break;
+            }
+            --device_index;
+        }
+        device = device->next;
+    }
+    return device;
+}
+
+static SDL_HIDAPI_Device *
+HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
+{
+    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+    while (device) {
+        if (device->vendor_id == vendor_id && device->product_id == product_id &&
+            SDL_strcmp(device->path, path) == 0) {
+            break;
+        }
+        device = device->next;
+    }
+    return device;
+}
+
+static void SDLCALL
+SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    int i;
+    SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+    SDL_bool enabled = (!hint || !*hint || ((*hint != '0') && (SDL_strcasecmp(hint, "false") != 0)));
+
+    if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
+        for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+            SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+            driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
+        }
+    } else {
+        for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+            SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+            if (SDL_strcmp(name, driver->hint) == 0) {
+                driver->enabled = enabled;
+                break;
+            }
+        }
+    }
+
+    /* Update device list if driver availability changes */
+    while (device) {
+        if (device->driver) {
+            if (!device->driver->enabled) {
+                device->driver = NULL;
+
+                --SDL_HIDAPI_numjoysticks;
+
+                SDL_PrivateJoystickRemoved(device->instance_id);
+            }
+        } else {
+            device->driver = HIDAPI_GetDeviceDriver(device);
+            if (device->driver) {
+                device->instance_id = SDL_GetNextJoystickInstanceID();
+
+                ++SDL_HIDAPI_numjoysticks;
+
+                SDL_PrivateJoystickAdded(device->instance_id);
+            }
+        }
+        device = device->next;
+    }
+}
+
+static void HIDAPI_JoystickDetect(void);
+
+static int
+HIDAPI_JoystickInit(void)
+{
+    int i;
+
+    if (hid_init() < 0) {
+        SDL_SetError("Couldn't initialize hidapi");
+        return -1;
+    }
+
+    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+        SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
+    }
+    SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
+                        SDL_HIDAPIDriverHintChanged, NULL);
+    HIDAPI_InitializeDiscovery();
+    HIDAPI_JoystickDetect();
+    return 0;
+}
+
+static int
+HIDAPI_JoystickGetCount(void)
+{
+    return SDL_HIDAPI_numjoysticks;
+}
+
+static void
+HIDAPI_AddDevice(struct hid_device_info *info)
+{
+    SDL_HIDAPI_Device *device;
+    SDL_HIDAPI_Device *curr, *last = NULL;
+
+    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
+        continue;
+    }
+
+    device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
+    if (!device) {
+        return;
+    }
+    device->instance_id = -1;
+    device->seen = SDL_TRUE;
+    device->vendor_id = info->vendor_id;
+    device->product_id = info->product_id;
+    device->version = info->release_number;
+    device->interface_number = info->interface_number;
+    device->usage_page = info->usage_page;
+    device->usage = info->usage;
+    {
+        /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
+        const Uint16 vendor = device->vendor_id;
+        const Uint16 product = device->product_id;
+        const Uint16 version = device->version;
+        Uint16 *guid16 = (Uint16 *)device->guid.data;
+
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(vendor);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(product);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(version);
+        *guid16++ = 0;
+
+        /* Note that this is a HIDAPI device for special handling elsewhere */
+        device->guid.data[14] = 'h';
+        device->guid.data[15] = 0;
+    }
+
+    /* Need the device name before getting the driver to know whether to ignore this device */
+    if (!device->name && info->manufacturer_string && info->product_string) {
+        char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
+        char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
+        if (!manufacturer_string && !product_string) {
+            if (sizeof(wchar_t) == sizeof(Uint16)) {
+                manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
+                product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
+            } else if (sizeof(wchar_t) == sizeof(Uint32)) {
+                manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
+                product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
+            }
+        }
+        if (manufacturer_string && product_string) {
+            size_t name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
+            device->name = (char *)SDL_malloc(name_size);
+            if (device->name) {
+                SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
+            }
+        }
+        if (manufacturer_string) {
+            SDL_free(manufacturer_string);
+        }
+        if (product_string) {
+            SDL_free(product_string);
+        }
+    }
+    if (!device->name) {
+        size_t name_size = (6 + 1 + 6 + 1);
+        device->name = (char *)SDL_malloc(name_size);
+        if (!device->name) {
+            SDL_free(device);
+            return;
+        }
+        SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
+    }
+
+    device->driver = HIDAPI_GetDeviceDriver(device);
+
+    if (device->driver) {
+        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
+        if (name) {
+            SDL_free(device->name);
+            device->name = SDL_strdup(name);
+        }
+    }
+
+    device->path = SDL_strdup(info->path);
+    if (!device->path) {
+        SDL_free(device->name);
+        SDL_free(device);
+        return;
+    }
+
+#ifdef DEBUG_HIDAPI
+    SDL_Log("Adding HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->driver ? device->driver->hint : "NONE");
+#endif
+
+    /* Add it to the list */
+    if (last) {
+        last->next = device;
+    } else {
+        SDL_HIDAPI_devices = device;
+    }
+
+    if (device->driver) {
+        /* It's a joystick! */
+        device->instance_id = SDL_GetNextJoystickInstanceID();
+
+        ++SDL_HIDAPI_numjoysticks;
+
+        SDL_PrivateJoystickAdded(device->instance_id);
+    }
+}
+
+
+static void
+HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
+{
+    SDL_HIDAPI_Device *curr, *last;
+    for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
+        if (curr == device) {
+            if (last) {
+                last->next = curr->next;
+            } else {
+                SDL_HIDAPI_devices = curr->next;
+            }
+
+            if (device->driver && send_event) {
+                /* Need to decrement the joystick count before we post the event */
+                --SDL_HIDAPI_numjoysticks;
+
+                SDL_PrivateJoystickRemoved(device->instance_id);
+            }
+
+            SDL_free(device->name);
+            SDL_free(device->path);
+            SDL_free(device);
+            return;
+        }
+    }
+}
+
+static void
+HIDAPI_UpdateDeviceList(void)
+{
+    SDL_HIDAPI_Device *device;
+    struct hid_device_info *devs, *info;
+
+    /* Prepare the existing device list */
+    device = SDL_HIDAPI_devices;
+    while (device) {
+        device->seen = SDL_FALSE;
+        device = device->next;
+    }
+
+    /* Enumerate the devices */
+    devs = hid_enumerate(0, 0);
+    if (devs) {
+        for (info = devs; info; info = info->next) {
+            device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
+            if (device) {
+                device->seen = SDL_TRUE;
+            } else {
+                HIDAPI_AddDevice(info);
+            }
+        }
+        hid_free_enumeration(devs);
+    }
+
+    /* Remove any devices that weren't seen */
+    device = SDL_HIDAPI_devices;
+    while (device) {
+        SDL_HIDAPI_Device *next = device->next;
+
+        if (!device->seen) {
+            HIDAPI_DelDevice(device, SDL_TRUE);
+        }
+        device = next;
+    }
+}
+
+SDL_bool
+HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
+{
+    SDL_HIDAPI_Device *device;
+
+    /* Don't update the device list for devices we know aren't supported */
+    if (!HIDAPI_IsDeviceSupported(vendor_id, product_id, version)) {
+        return SDL_FALSE;
+    }
+
+    /* Make sure the device list is completely up to date when we check for device presence */
+    HIDAPI_UpdateDeviceList();
+
+    device = SDL_HIDAPI_devices;
+    while (device) {
+        if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
+            return SDL_TRUE;
+        }
+        device = device->next;
+    }
+    return SDL_FALSE;
+}
+
+static void
+HIDAPI_JoystickDetect(void)
+{
+    HIDAPI_UpdateDiscovery();
+    if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
+        /* FIXME: We probably need to schedule an update in a few seconds as well */
+        HIDAPI_UpdateDeviceList();
+        SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
+    }
+}
+
+static const char *
+HIDAPI_JoystickGetDeviceName(int device_index)
+{
+    return HIDAPI_GetJoystickByIndex(device_index)->name;
+}
+
+static int
+HIDAPI_JoystickGetDevicePlayerIndex(int device_index)
+{
+    return -1;
+}
+
+static SDL_JoystickGUID
+HIDAPI_JoystickGetDeviceGUID(int device_index)
+{
+    return HIDAPI_GetJoystickByIndex(device_index)->guid;
+}
+
+static SDL_JoystickID
+HIDAPI_JoystickGetDeviceInstanceID(int device_index)
+{
+    return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
+}
+
+static int
+HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
+{
+    SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
+    struct joystick_hwdata *hwdata;
+
+    hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
+    if (!hwdata) {
+        return SDL_OutOfMemory();
+    }
+
+    hwdata->driver = device->driver;
+    hwdata->dev = hid_open_path(device->path, 0);
+    if (!hwdata->dev) {
+        SDL_free(hwdata);
+        return SDL_SetError("Couldn't open HID device %s", device->path);
+    }
+    hwdata->mutex = SDL_CreateMutex();
+
+    if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
+        hid_close(hwdata->dev);
+        SDL_free(hwdata);
+        return -1;
+    }
+
+    joystick->hwdata = hwdata;
+    return 0;
+}
+
+static int
+HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    struct joystick_hwdata *hwdata = joystick->hwdata;
+    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
+    int result;
+
+    SDL_LockMutex(hwdata->mutex);
+    result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
+    SDL_UnlockMutex(hwdata->mutex);
+    return result;
+}
+
+static void
+HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
+{
+    struct joystick_hwdata *hwdata = joystick->hwdata;
+    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
+    SDL_bool succeeded;
+
+    SDL_LockMutex(hwdata->mutex);
+    succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
+    SDL_UnlockMutex(hwdata->mutex);
+    
+    if (!succeeded) {
+        SDL_HIDAPI_Device *device;
+        for (device = SDL_HIDAPI_devices; device; device = device->next) {
+            if (device->instance_id == joystick->instance_id) {
+                HIDAPI_DelDevice(device, SDL_TRUE);
+                break;
+            }
+        }
+    }
+}
+
+static void
+HIDAPI_JoystickClose(SDL_Joystick * joystick)
+{
+    struct joystick_hwdata *hwdata = joystick->hwdata;
+    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
+    driver->Quit(joystick, hwdata->dev, hwdata->context);
+
+    hid_close(hwdata->dev);
+    SDL_DestroyMutex(hwdata->mutex);
+    SDL_free(hwdata);
+    joystick->hwdata = NULL;
+}
+
+static void
+HIDAPI_JoystickQuit(void)
+{
+    int i;
+
+    HIDAPI_ShutdownDiscovery();
+
+    while (SDL_HIDAPI_devices) {
+        HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
+    }
+    for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+        SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+        SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
+    }
+    SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
+                        SDL_HIDAPIDriverHintChanged, NULL);
+    SDL_HIDAPI_numjoysticks = 0;
+
+    hid_exit();
+}
+
+SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
+{
+    HIDAPI_JoystickInit,
+    HIDAPI_JoystickGetCount,
+    HIDAPI_JoystickDetect,
+    HIDAPI_JoystickGetDeviceName,
+    HIDAPI_JoystickGetDevicePlayerIndex,
+    HIDAPI_JoystickGetDeviceGUID,
+    HIDAPI_JoystickGetDeviceInstanceID,
+    HIDAPI_JoystickOpen,
+    HIDAPI_JoystickRumble,
+    HIDAPI_JoystickUpdate,
+    HIDAPI_JoystickClose,
+    HIDAPI_JoystickQuit,
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/hidapi/SDL_hidapijoystick_c.h b/source/src/joystick/hidapi/SDL_hidapijoystick_c.h
new file mode 100644
index 0000000..18a4483
--- /dev/null
+++ b/source/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -0,0 +1,74 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef SDL_JOYSTICK_HIDAPI_H
+#define SDL_JOYSTICK_HIDAPI_H
+
+#include "../../hidapi/hidapi/hidapi.h"
+
+/* This is the full set of HIDAPI drivers available */
+#define SDL_JOYSTICK_HIDAPI_PS4
+#define SDL_JOYSTICK_HIDAPI_SWITCH
+#define SDL_JOYSTICK_HIDAPI_XBOX360
+#define SDL_JOYSTICK_HIDAPI_XBOXONE
+
+#ifdef __WINDOWS__
+/* On Windows, Xbox One controllers are handled by the Xbox 360 driver */
+#undef SDL_JOYSTICK_HIDAPI_XBOXONE
+/* It turns out HIDAPI for Xbox controllers doesn't allow background input */
+#undef SDL_JOYSTICK_HIDAPI_XBOX360
+#endif
+
+#ifdef __MACOSX__
+/* On Mac OS X, Xbox One controllers are handled by the Xbox 360 driver */
+#undef SDL_JOYSTICK_HIDAPI_XBOXONE
+#endif
+
+typedef struct _SDL_HIDAPI_DeviceDriver
+{
+    const char *hint;
+    SDL_bool enabled;
+    SDL_bool (*IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number);
+    const char *(*GetDeviceName)(Uint16 vendor_id, Uint16 product_id);
+    SDL_bool (*Init)(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context);
+    int (*Rumble)(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+    SDL_bool (*Update)(SDL_Joystick *joystick, hid_device *dev, void *context);
+    void (*Quit)(SDL_Joystick *joystick, hid_device *dev, void *context);
+
+} SDL_HIDAPI_DeviceDriver;
+
+/* HIDAPI device support */
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne;
+
+/* Return true if a HID device is present and supported as a joystick */
+extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version);
+
+/* Return the name of an Xbox 360 or Xbox One controller */
+extern const char *HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id);
+
+#endif /* SDL_JOYSTICK_HIDAPI_H */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/iphoneos/SDL_sysjoystick.m b/source/src/joystick/iphoneos/SDL_sysjoystick.m
index d601498..d85efad 100644
--- a/source/src/joystick/iphoneos/SDL_sysjoystick.m
+++ b/source/src/joystick/iphoneos/SDL_sysjoystick.m
@@ -33,7 +33,6 @@
 #include "SDL_stdinc.h"
 #include "../SDL_sysjoystick.h"
 #include "../SDL_joystick_c.h"
-#include "../steam/SDL_steamcontroller.h"
 
 
 #if !SDL_EVENTS_DISABLED
@@ -59,7 +58,6 @@
 static SDL_JoystickDeviceItem *deviceList = NULL;
 
 static int numjoysticks = 0;
-static SDL_JoystickID instancecounter = 0;
 int SDL_AppleTVRemoteOpenedAsJoystick = 0;
 
 static SDL_JoystickDeviceItem *
@@ -80,9 +78,16 @@
 }
 
 static void
-SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
+IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
 {
 #ifdef SDL_JOYSTICK_MFI
+    const Uint16 VENDOR_APPLE = 0x05AC;
+    Uint16 *guid16 = (Uint16 *)device->guid.data;
+    Uint16 vendor = 0;
+    Uint16 product = 0;
+    Uint16 version = 0;
+    Uint8 subtype = 0;
+
     const char *name = NULL;
     /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
      * struct, and ARC doesn't work with structs. */
@@ -98,40 +103,26 @@
 
     device->name = SDL_strdup(name);
 
-    device->guid.data[0] = 'M';
-    device->guid.data[1] = 'F';
-    device->guid.data[2] = 'i';
-    device->guid.data[3] = 'G';
-    device->guid.data[4] = 'a';
-    device->guid.data[5] = 'm';
-    device->guid.data[6] = 'e';
-    device->guid.data[7] = 'p';
-    device->guid.data[8] = 'a';
-    device->guid.data[9] = 'd';
-
     if (controller.extendedGamepad) {
-        device->guid.data[10] = 1;
-    } else if (controller.gamepad) {
-        device->guid.data[10] = 2;
-    }
-#if TARGET_OS_TV
-    else if (controller.microGamepad) {
-        device->guid.data[10] = 3;
-        device->remote = SDL_TRUE;
-    }
-#endif /* TARGET_OS_TV */
-
-    if (controller.extendedGamepad) {
+        vendor = VENDOR_APPLE;
+        product = 1;
+        subtype = 1;
         device->naxes = 6; /* 2 thumbsticks and 2 triggers */
         device->nhats = 1; /* d-pad */
         device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
     } else if (controller.gamepad) {
+        vendor = VENDOR_APPLE;
+        product = 2;
+        subtype = 2;
         device->naxes = 0; /* no traditional analog inputs */
         device->nhats = 1; /* d-pad */
         device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
     }
 #if TARGET_OS_TV
     else if (controller.microGamepad) {
+        vendor = VENDOR_APPLE;
+        product = 3;
+        subtype = 3;
         device->naxes = 2; /* treat the touch surface as two axes */
         device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
         device->nbuttons = 3; /* AX, pause button */
@@ -139,6 +130,21 @@
         controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
     }
 #endif /* TARGET_OS_TV */
+
+    /* We only need 16 bits for each of these; space them out to fill 128. */
+    /* Byteswap so devices get same GUID on little/big endian platforms. */
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
+    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(vendor);
+    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(product);
+    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(version);
+    *guid16++ = 0;
+
+    /* Note that this is an MFI controller and what subtype it is */
+    device->guid.data[14] = 'm';
+    device->guid.data[15] = subtype;
 
     /* This will be set when the first button press of the controller is
      * detected. */
@@ -148,7 +154,7 @@
 }
 
 static void
-SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
+IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
 {
     SDL_JoystickDeviceItem *device = deviceList;
 
@@ -174,7 +180,7 @@
     }
 
     device->accelerometer = accelerometer;
-    device->instance_id = instancecounter++;
+    device->instance_id = SDL_GetNextJoystickInstanceID();
 
     if (accelerometer) {
 #if TARGET_OS_TV
@@ -190,7 +196,7 @@
         SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
 #endif /* TARGET_OS_TV */
     } else if (controller) {
-        SDL_SYS_AddMFIJoystickDevice(device, controller);
+        IOS_AddMFIJoystickDevice(device, controller);
     }
 
     if (deviceList == NULL) {
@@ -205,11 +211,11 @@
 
     ++numjoysticks;
 
-    SDL_PrivateJoystickAdded(numjoysticks - 1);
+    SDL_PrivateJoystickAdded(device->instance_id);
 }
 
 static SDL_JoystickDeviceItem *
-SDL_SYS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
+IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
 {
     SDL_JoystickDeviceItem *prev = NULL;
     SDL_JoystickDeviceItem *next = NULL;
@@ -278,78 +284,27 @@
 }
 #endif /* TARGET_OS_TV */
 
-static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
-{
-    SDL_JoystickDeviceItem *device = (SDL_JoystickDeviceItem *)SDL_calloc(1, sizeof(SDL_JoystickDeviceItem));
-    if (device == NULL) {
-        return SDL_FALSE;
-    }
-
-    *device_instance = device->instance_id = instancecounter++;
-    device->name = SDL_strdup(name);
-    device->guid = guid;
-    SDL_GetSteamControllerInputs(&device->nbuttons,
-                                 &device->naxes,
-                                 &device->nhats);
-    device->m_bSteamController = SDL_TRUE;
-
-    if (deviceList == NULL) {
-        deviceList = device;
-    } else {
-        SDL_JoystickDeviceItem *lastdevice = deviceList;
-        while (lastdevice->next != NULL) {
-            lastdevice = lastdevice->next;
-        }
-        lastdevice->next = device;
-    }
-
-    ++numjoysticks;
-
-    SDL_PrivateJoystickAdded(numjoysticks - 1);
-
-    return SDL_TRUE;
-}
-
-static void SteamControllerDisconnectedCallback(int device_instance)
-{
-    SDL_JoystickDeviceItem *item;
-
-    for (item = deviceList; item; item = item->next) {
-        if (item->instance_id == device_instance) {
-            SDL_SYS_RemoveJoystickDevice(item);
-            break;
-        }
-    }
-}
-
-/* Function to scan the system for joysticks.
- * Joystick 0 should be the system default joystick.
- * It should return 0, or -1 on an unrecoverable fatal error.
- */
-int
-SDL_SYS_JoystickInit(void)
+static int
+IOS_JoystickInit(void)
 {
     @autoreleasepool {
         NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
 
-        SDL_InitSteamControllers(SteamControllerConnectedCallback,
-                                 SteamControllerDisconnectedCallback);
-
 #if !TARGET_OS_TV
         if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
             /* Default behavior, accelerometer as joystick */
-            SDL_SYS_AddJoystickDevice(nil, SDL_TRUE);
+            IOS_AddJoystickDevice(nil, SDL_TRUE);
         }
 #endif /* !TARGET_OS_TV */
 
 #ifdef SDL_JOYSTICK_MFI
         /* GameController.framework was added in iOS 7. */
         if (![GCController class]) {
-            return numjoysticks;
+            return 0;
         }
 
         for (GCController *controller in [GCController controllers]) {
-            SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
+            IOS_AddJoystickDevice(controller, SDL_FALSE);
         }
 
 #if TARGET_OS_TV
@@ -362,7 +317,7 @@
                                                queue:nil
                                           usingBlock:^(NSNotification *note) {
                                               GCController *controller = note.object;
-                                              SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
+                                              IOS_AddJoystickDevice(controller, SDL_FALSE);
                                           }];
 
         disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
@@ -373,7 +328,7 @@
                                                  SDL_JoystickDeviceItem *device = deviceList;
                                                  while (device != NULL) {
                                                      if (device->controller == controller) {
-                                                         SDL_SYS_RemoveJoystickDevice(device);
+                                                         IOS_RemoveJoystickDevice(device);
                                                          break;
                                                      }
                                                      device = device->next;
@@ -382,43 +337,55 @@
 #endif /* SDL_JOYSTICK_MFI */
     }
 
-    return numjoysticks;
+    return 0;
 }
 
-int
-SDL_SYS_NumJoysticks(void)
+static int
+IOS_JoystickGetCount(void)
 {
     return numjoysticks;
 }
 
-void
-SDL_SYS_JoystickDetect(void)
+static void
+IOS_JoystickDetect(void)
 {
-    SDL_UpdateSteamControllers();
 }
 
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+IOS_JoystickGetDeviceName(int device_index)
 {
     SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
     return device ? device->name : "Unknown";
 }
 
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static int
+IOS_JoystickGetDevicePlayerIndex(int device_index)
 {
-    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
-    return device ? device->instance_id : 0;
+    return -1;
 }
 
-/* Function to open a joystick for use.
-   The joystick to open is specified by the device index.
-   This should fill the nbuttons and naxes fields of the joystick structure.
-   It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static SDL_JoystickGUID
+IOS_JoystickGetDeviceGUID( int device_index )
+{
+    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
+    SDL_JoystickGUID guid;
+    if (device) {
+        guid = device->guid;
+    } else {
+        SDL_zero(guid);
+    }
+    return guid;
+}
+
+static SDL_JoystickID
+IOS_JoystickGetDeviceInstanceID(int device_index)
+{
+    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
+    return device ? device->instance_id : -1;
+}
+
+static int
+IOS_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
     if (device == NULL) {
@@ -464,15 +431,8 @@
     return 0;
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool
-SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
-{
-    return joystick->hwdata != NULL;
-}
-
 static void
-SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
+IOS_AccelerometerUpdate(SDL_Joystick * joystick)
 {
 #if !TARGET_OS_TV
     const float maxgforce = SDL_IPHONE_MAX_GFORCE;
@@ -517,7 +477,7 @@
 
 #ifdef SDL_JOYSTICK_MFI
 static Uint8
-SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
+IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
 {
     Uint8 hat = 0;
 
@@ -542,7 +502,7 @@
 #endif
 
 static void
-SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
+IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
 {
 #if SDL_JOYSTICK_MFI
     @autoreleasepool {
@@ -572,7 +532,7 @@
                 gamepad.rightShoulder.isPressed,
             };
 
-            hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
+            hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
 
             for (i = 0; i < SDL_arraysize(axes); i++) {
                 /* The triggers (axes 2 and 5) are resting at -32768 but SDL
@@ -599,7 +559,7 @@
                 gamepad.rightShoulder.isPressed,
             };
 
-            hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
+            hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
 
             for (i = 0; i < SDL_arraysize(buttons); i++) {
                 updateplayerindex |= (joystick->buttons[i] != buttons[i]);
@@ -638,15 +598,11 @@
         }
 
         for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
-            /* The pause button is always last. */
-            Uint8 pausebutton = joystick->nbuttons - 1;
-
+            const Uint8 pausebutton = joystick->nbuttons - 1; /* The pause button is always last. */
             SDL_PrivateJoystickButton(joystick, pausebutton, SDL_PRESSED);
             SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
-
             updateplayerindex = YES;
         }
-
         joystick->hwdata->num_pause_presses = 0;
 
         if (updateplayerindex && controller.playerIndex == -1) {
@@ -673,13 +629,14 @@
 #endif /* SDL_JOYSTICK_MFI */
 }
 
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static int
+IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    return SDL_Unsupported();
+}
+
+static void
+IOS_JoystickUpdate(SDL_Joystick * joystick)
 {
     SDL_JoystickDeviceItem *device = joystick->hwdata;
 
@@ -687,21 +644,15 @@
         return;
     }
     
-    if (device->m_bSteamController) {
-        SDL_UpdateSteamController(joystick);
-        return;
-    }
-
     if (device->accelerometer) {
-        SDL_SYS_AccelerometerUpdate(joystick);
+        IOS_AccelerometerUpdate(joystick);
     } else if (device->controller) {
-        SDL_SYS_MFIJoystickUpdate(joystick);
+        IOS_MFIJoystickUpdate(joystick);
     }
 }
 
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+IOS_JoystickClose(SDL_Joystick * joystick)
 {
     SDL_JoystickDeviceItem *device = joystick->hwdata;
 
@@ -729,9 +680,8 @@
     }
 }
 
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+IOS_JoystickQuit(void)
 {
     @autoreleasepool {
 #ifdef SDL_JOYSTICK_MFI
@@ -754,7 +704,7 @@
 #endif /* SDL_JOYSTICK_MFI */
 
         while (deviceList != NULL) {
-            SDL_SYS_RemoveJoystickDevice(deviceList);
+            IOS_RemoveJoystickDevice(deviceList);
         }
 
 #if !TARGET_OS_TV
@@ -762,34 +712,23 @@
 #endif /* !TARGET_OS_TV */
     }
 
-    SDL_QuitSteamControllers();
-
     numjoysticks = 0;
 }
 
-SDL_JoystickGUID
-SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_IOS_JoystickDriver =
 {
-    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
-    SDL_JoystickGUID guid;
-    if (device) {
-        guid = device->guid;
-    } else {
-        SDL_zero(guid);
-    }
-    return guid;
-}
-
-SDL_JoystickGUID
-SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
-    SDL_JoystickGUID guid;
-    if (joystick->hwdata) {
-        guid = joystick->hwdata->guid;
-    } else {
-        SDL_zero(guid);
-    }
-    return guid;
-}
+    IOS_JoystickInit,
+    IOS_JoystickGetCount,
+    IOS_JoystickDetect,
+    IOS_JoystickGetDeviceName,
+    IOS_JoystickGetDevicePlayerIndex,
+    IOS_JoystickGetDeviceGUID,
+    IOS_JoystickGetDeviceInstanceID,
+    IOS_JoystickOpen,
+    IOS_JoystickRumble,
+    IOS_JoystickUpdate,
+    IOS_JoystickClose,
+    IOS_JoystickQuit,
+};
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/iphoneos/SDL_sysjoystick_c.h b/source/src/joystick/iphoneos/SDL_sysjoystick_c.h
index 7be5b04..12aa296 100644
--- a/source/src/joystick/iphoneos/SDL_sysjoystick_c.h
+++ b/source/src/joystick/iphoneos/SDL_sysjoystick_c.h
@@ -35,6 +35,7 @@
 
     GCController __unsafe_unretained *controller;
     int num_pause_presses;
+    Uint32 pause_button_down_time;
 
     char *name;
     SDL_Joystick *joystick;
@@ -44,9 +45,6 @@
     int naxes;
     int nbuttons;
     int nhats;
-
-    /* Steam Controller support */
-    SDL_bool m_bSteamController;
 
     struct joystick_hwdata *next;
 } joystick_hwdata;
diff --git a/source/src/joystick/linux/SDL_sysjoystick.c b/source/src/joystick/linux/SDL_sysjoystick.c
index 457c4b8..06a2d9a 100644
--- a/source/src/joystick/linux/SDL_sysjoystick.c
+++ b/source/src/joystick/linux/SDL_sysjoystick.c
@@ -29,10 +29,11 @@
 /* This is the Linux implementation of the SDL joystick API */
 
 #include <sys/stat.h>
-#include <unistd.h>
+#include <errno.h>              /* errno, strerror */
 #include <fcntl.h>
-#include <sys/ioctl.h>
 #include <limits.h>             /* For the definition of PATH_MAX */
+#include <sys/ioctl.h>
+#include <unistd.h>
 #include <linux/joystick.h>
 
 #include "SDL_assert.h"
@@ -43,6 +44,7 @@
 #include "../SDL_joystick_c.h"
 #include "../steam/SDL_steamcontroller.h"
 #include "SDL_sysjoystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
 
 /* This isn't defined in older Linux kernel headers */
 #ifndef SYN_DROPPED
@@ -76,7 +78,6 @@
 static SDL_joylist_item *SDL_joylist = NULL;
 static SDL_joylist_item *SDL_joylist_tail = NULL;
 static int numjoysticks = 0;
-static int instance_counter = 0;
 
 
 #define test_bit(nr, addr) \
@@ -209,6 +210,13 @@
         return 0;
     }
 
+#ifdef SDL_JOYSTICK_HIDAPI
+    if (HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version)) {
+        /* The HIDAPI driver is taking care of this device */
+        return 0;
+    }
+#endif
+
     /* Check the joystick blacklist */
     id = MAKE_VIDPID(inpid.vendor, inpid.product);
     for (i = 0; i < SDL_arraysize(joystick_blacklist); ++i) {
@@ -239,8 +247,7 @@
         SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
     }
 
-    if (SDL_IsGameControllerNameAndGUID(namebuf, *guid) &&
-        SDL_ShouldIgnoreGameController(namebuf, *guid)) {
+    if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) {
         return 0;
     }
     return 1;
@@ -325,14 +332,14 @@
     item->name = SDL_strdup(namebuf);
     item->guid = guid;
 
-    if ( (item->path == NULL) || (item->name == NULL) ) {
+    if ((item->path == NULL) || (item->name == NULL)) {
          SDL_free(item->path);
          SDL_free(item->name);
          SDL_free(item);
          return -1;
     }
 
-    item->device_instance = instance_counter++;
+    item->device_instance = SDL_GetNextJoystickInstanceID();
     if (SDL_joylist_tail == NULL) {
         SDL_joylist = SDL_joylist_tail = item;
     } else {
@@ -343,7 +350,7 @@
     /* Need to increment the joystick count before we post the event */
     ++numjoysticks;
 
-    SDL_PrivateJoystickAdded(numjoysticks - 1);
+    SDL_PrivateJoystickAdded(item->device_instance);
 
     return numjoysticks;
 }
@@ -409,7 +416,7 @@
         MaybeAddDevice(path);
     }
 
-    return numjoysticks;
+    return 0;
 }
 #endif
 
@@ -430,7 +437,7 @@
     /* Force a scan to build the initial device list */
     SDL_UDEV_Scan();
 
-    return numjoysticks;
+    return 0;
 }
 #endif
 
@@ -455,7 +462,7 @@
          return SDL_FALSE;
     }
 
-    *device_instance = item->device_instance = instance_counter++;
+    *device_instance = item->device_instance = SDL_GetNextJoystickInstanceID();
     if (SDL_joylist_tail == NULL) {
         SDL_joylist = SDL_joylist_tail = item;
     } else {
@@ -466,7 +473,7 @@
     /* Need to increment the joystick count before we post the event */
     ++numjoysticks;
 
-    SDL_PrivateJoystickAdded(numjoysticks - 1);
+    SDL_PrivateJoystickAdded(item->device_instance);
 
     return SDL_TRUE;
 }
@@ -505,8 +512,8 @@
     }
 }
 
-int
-SDL_SYS_JoystickInit(void)
+static int
+LINUX_JoystickInit(void)
 {
     /* First see if the user specified one or more joysticks to use */
     if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
@@ -534,14 +541,14 @@
 #endif
 }
 
-int
-SDL_SYS_NumJoysticks(void)
+static int
+LINUX_JoystickGetCount(void)
 {
     return numjoysticks;
 }
 
-void
-SDL_SYS_JoystickDetect(void)
+static void
+LINUX_JoystickDetect(void)
 {
 #if SDL_USE_LIBUDEV
     SDL_UDEV_Poll();
@@ -569,14 +576,27 @@
 }
 
 /* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+LINUX_JoystickGetDeviceName(int device_index)
 {
     return JoystickByDevIndex(device_index)->name;
 }
 
+static int
+LINUX_JoystickGetDevicePlayerIndex(int device_index)
+{
+    return -1;
+}
+
+static SDL_JoystickGUID
+LINUX_JoystickGetDeviceGUID( int device_index )
+{
+    return JoystickByDevIndex(device_index)->guid;
+}
+
 /* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickID
+LINUX_JoystickGetDeviceInstanceID(int device_index)
 {
     return JoystickByDevIndex(device_index)->device_instance;
 }
@@ -624,6 +644,7 @@
     unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
     unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
     unsigned long relbit[NBITS(REL_MAX)] = { 0 };
+    unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
 
     /* See if this device uses the new unified event API */
     if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
@@ -719,6 +740,15 @@
             }
         }
     }
+
+    if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
+        if (test_bit(FF_RUMBLE, ffbit)) {
+            joystick->hwdata->ff_rumble = SDL_TRUE;
+        }
+        if (test_bit(FF_SINE, ffbit)) {
+            joystick->hwdata->ff_sine = SDL_TRUE;
+        }
+    }
 }
 
 
@@ -727,8 +757,8 @@
    This should fill the nbuttons and naxes fields of the joystick structure.
    It returns 0, or -1 if there is an error.
  */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     SDL_joylist_item *item = JoystickByDevIndex(device_index);
 
@@ -744,6 +774,7 @@
     }
     joystick->hwdata->item = item;
     joystick->hwdata->guid = item->guid;
+    joystick->hwdata->effect.id = -1;
     joystick->hwdata->m_bSteamController = item->m_bSteamController;
 
     if (item->m_bSteamController) {
@@ -752,7 +783,7 @@
                                      &joystick->naxes,
                                      &joystick->nhats);
     } else {
-        int fd = open(item->path, O_RDONLY, 0);
+        int fd = open(item->path, O_RDWR, 0);
         if (fd < 0) {
             SDL_free(joystick->hwdata);
             joystick->hwdata = NULL;
@@ -784,10 +815,42 @@
     return (0);
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static int
+LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
 {
-    return joystick->hwdata->item != NULL;
+    struct input_event event;
+
+    if (joystick->hwdata->ff_rumble) {
+        struct ff_effect *effect = &joystick->hwdata->effect;
+
+        effect->type = FF_RUMBLE;
+        effect->replay.length = SDL_min(duration_ms, 32767);
+        effect->u.rumble.strong_magnitude = low_frequency_rumble;
+        effect->u.rumble.weak_magnitude = high_frequency_rumble;
+    } else if (joystick->hwdata->ff_sine) {
+        /* Scale and average the two rumble strengths */
+        Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+        struct ff_effect *effect = &joystick->hwdata->effect;
+
+        effect->type = FF_PERIODIC;
+        effect->replay.length = SDL_min(duration_ms, 32767);
+        effect->u.periodic.waveform = FF_SINE;
+        effect->u.periodic.magnitude = magnitude;
+    } else {
+        return SDL_Unsupported();
+    }
+
+    if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
+        return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));
+    }
+
+    event.type = EV_FF;
+    event.code = joystick->hwdata->effect.id;
+    event.value = 1;
+    if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {
+        return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));
+    }
+    return 0;
 }
 
 static SDL_INLINE void
@@ -963,8 +1026,8 @@
     }
 }
 
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static void
+LINUX_JoystickUpdate(SDL_Joystick * joystick)
 {
     int i;
 
@@ -990,10 +1053,14 @@
 }
 
 /* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+LINUX_JoystickClose(SDL_Joystick * joystick)
 {
     if (joystick->hwdata) {
+        if (joystick->hwdata->effect.id >= 0) {
+            ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
+            joystick->hwdata->effect.id = -1;
+        }
         if (joystick->hwdata->fd >= 0) {
             close(joystick->hwdata->fd);
         }
@@ -1008,8 +1075,8 @@
 }
 
 /* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+LINUX_JoystickQuit(void)
 {
     SDL_joylist_item *item = NULL;
     SDL_joylist_item *next = NULL;
@@ -1024,7 +1091,6 @@
     SDL_joylist = SDL_joylist_tail = NULL;
 
     numjoysticks = 0;
-    instance_counter = 0;
 
 #if SDL_USE_LIBUDEV
     SDL_UDEV_DelCallback(joystick_udev_callback);
@@ -1034,15 +1100,21 @@
     SDL_QuitSteamControllers();
 }
 
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_LINUX_JoystickDriver =
 {
-    return JoystickByDevIndex(device_index)->guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
-    return joystick->hwdata->guid;
-}
+    LINUX_JoystickInit,
+    LINUX_JoystickGetCount,
+    LINUX_JoystickDetect,
+    LINUX_JoystickGetDeviceName,
+    LINUX_JoystickGetDevicePlayerIndex,
+    LINUX_JoystickGetDeviceGUID,
+    LINUX_JoystickGetDeviceInstanceID,
+    LINUX_JoystickOpen,
+    LINUX_JoystickRumble,
+    LINUX_JoystickUpdate,
+    LINUX_JoystickClose,
+    LINUX_JoystickQuit,
+};
 
 #endif /* SDL_JOYSTICK_LINUX */
 
diff --git a/source/src/joystick/linux/SDL_sysjoystick_c.h b/source/src/joystick/linux/SDL_sysjoystick_c.h
index d06b387..83d5937 100644
--- a/source/src/joystick/linux/SDL_sysjoystick_c.h
+++ b/source/src/joystick/linux/SDL_sysjoystick_c.h
@@ -19,6 +19,9 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_sysjoystick_c_h_
+#define SDL_sysjoystick_c_h_
+
 #include <linux/input.h>
 
 struct SDL_joylist_item;
@@ -30,6 +33,10 @@
     struct SDL_joylist_item *item;
     SDL_JoystickGUID guid;
     char *fname;                /* Used in haptic subsystem */
+
+    SDL_bool ff_rumble;
+    SDL_bool ff_sine;
+    struct ff_effect effect;
 
     /* The current Linux joystick driver maps hats to two axes */
     struct hwdata_hat
@@ -57,4 +64,6 @@
     SDL_bool m_bSteamController;
 };
 
+#endif /* SDL_sysjoystick_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/psp/SDL_sysjoystick.c b/source/src/joystick/psp/SDL_sysjoystick.c
index 228cbb2..262da85 100644
--- a/source/src/joystick/psp/SDL_sysjoystick.c
+++ b/source/src/joystick/psp/SDL_sysjoystick.c
@@ -177,12 +177,6 @@
     return 0;
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
-{
-    return SDL_TRUE;
-}
-
 /* Function to update the state of a joystick - called as a device poll.
  * This function shouldn't update the joystick structure directly,
  * but instead should call SDL_PrivateJoystick*() to deliver events
diff --git a/source/src/joystick/sort_controllers.py b/source/src/joystick/sort_controllers.py
index 47213c2..32f065a 100755
--- a/source/src/joystick/sort_controllers.py
+++ b/source/src/joystick/sort_controllers.py
@@ -27,16 +27,28 @@
 def write_controllers():
     global controllers
     global controller_guids
-    for entry in sorted(controllers, key=lambda entry: entry[2]):
+    # Check for duplicates
+    for entry in controllers:
+        if (entry[1] in controller_guids):
+            current_name = entry[2]
+            existing_name = controller_guids[entry[1]][2]
+            print("Warning: entry '%s' is duplicate of entry '%s'" % (current_name, existing_name))
+
+            if (not current_name.startswith("(DUPE)")):
+                entry[2] = "(DUPE) " + current_name
+
+            if (not existing_name.startswith("(DUPE)")):
+                controller_guids[entry[1]][2] = "(DUPE) " + existing_name
+
+        controller_guids[entry[1]] = entry
+
+    for entry in sorted(controllers, key=lambda entry: entry[2]+"-"+entry[1]):
         line = "".join(entry) + "\n"
         line = line.replace("\t", "    ")
         if not line.endswith(",\n") and not line.endswith("*/\n"):
             print("Warning: '%s' is missing a comma at the end of the line" % (line))
-        if (entry[1] in controller_guids):
-            print("Warning: entry '%s' is duplicate of entry '%s'" % (entry[2], controller_guids[entry[1]][2]))
-        controller_guids[entry[1]] = entry
-
         output.write(line)
+
     controllers = []
     controller_guids = {}
 
diff --git a/source/src/joystick/steam/SDL_steamcontroller.h b/source/src/joystick/steam/SDL_steamcontroller.h
index ce37b4d..81b8879 100644
--- a/source/src/joystick/steam/SDL_steamcontroller.h
+++ b/source/src/joystick/steam/SDL_steamcontroller.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_steamcontroller_h_
+#define SDL_steamcontroller_h_
+
 #include "../../SDL_internal.h"
 
 typedef SDL_bool (*SteamControllerConnectedCallback_t)(const char *name, SDL_JoystickGUID guid, int *device_instance);
@@ -30,4 +34,6 @@
 void SDL_UpdateSteamController(SDL_Joystick *joystick);
 void SDL_QuitSteamControllers(void);
 
+#endif /* SDL_steamcontroller_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/windows/SDL_dinputjoystick.c b/source/src/joystick/windows/SDL_dinputjoystick.c
index cff868b..67d7d25 100644
--- a/source/src/joystick/windows/SDL_dinputjoystick.c
+++ b/source/src/joystick/windows/SDL_dinputjoystick.c
@@ -27,6 +27,7 @@
 #include "SDL_windowsjoystick_c.h"
 #include "SDL_dinputjoystick_c.h"
 #include "SDL_xinputjoystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
 
 #ifndef DIDFT_OPTIONAL
 #define DIDFT_OPTIONAL      0x80000000
@@ -34,6 +35,8 @@
 
 #define INPUT_QSIZE 32      /* Buffer up to 32 input messages */
 #define JOY_AXIS_THRESHOLD  (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/100)   /* 1% motion */
+
+#define CONVERT_MAGNITUDE(x)    (((x)*10000) / 0x7FFF)
 
 /* external variables referenced. */
 extern HWND SDL_HelperWindow;
@@ -46,170 +49,170 @@
 
 /* Taken from Wine - Thanks! */
 static DIOBJECTDATAFORMAT dfDIJoystick2[] = {
-        { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
-        { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+    { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
 };
 
 const DIDATAFORMAT SDL_c_dfDIJoystick2 = {
@@ -311,6 +314,61 @@
     return SDL_FALSE;
 }
 
+void FreeRumbleEffectData(DIEFFECT *effect)
+{
+    if (!effect) {
+        return;
+    }
+    SDL_free(effect->rgdwAxes);
+    SDL_free(effect->rglDirection);
+    SDL_free(effect->lpvTypeSpecificParams);
+    SDL_free(effect);
+}
+
+DIEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
+{
+    DIEFFECT *effect;
+    DIPERIODIC *periodic;
+
+    /* Create the effect */
+    effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));
+    if (!effect) {
+        return NULL;
+    }
+    effect->dwSize = sizeof(*effect);
+    effect->dwGain = 10000;
+    effect->dwFlags = DIEFF_OBJECTOFFSETS;
+    effect->dwDuration = duration_ms * 1000; /* In microseconds. */
+    effect->dwTriggerButton = DIEB_NOTRIGGER;
+
+    effect->cAxes = 2;
+    effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
+    if (!effect->rgdwAxes) {
+        FreeRumbleEffectData(effect);
+        return NULL;
+    }
+
+    effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
+    if (!effect->rglDirection) {
+        FreeRumbleEffectData(effect);
+        return NULL;
+    }
+    effect->dwFlags |= DIEFF_CARTESIAN;
+
+    periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));
+    if (!periodic) {
+        FreeRumbleEffectData(effect);
+        return NULL;
+    }
+    periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+    periodic->dwPeriod = 1000000;
+
+    effect->cbTypeSpecificParams = sizeof(*periodic);
+    effect->lpvTypeSpecificParams = periodic;
+
+    return effect;
+}
+
 int
 SDL_DINPUT_JoystickInit(void)
 {
@@ -348,28 +406,29 @@
 static BOOL CALLBACK
 EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
 {
-    const Uint16 BUS_USB = 0x03;
-    const Uint16 BUS_BLUETOOTH = 0x05;
     JoyStick_DeviceData *pNewJoystick;
     JoyStick_DeviceData *pPrevJoystick = NULL;
     const DWORD devtype = (pdidInstance->dwDevType & 0xFF);
     Uint16 *guid16;
+    Uint16 vendor = 0;
+    Uint16 product = 0;
+    Uint16 version = 0;
     WCHAR hidPath[MAX_PATH];
 
     if (devtype == DI8DEVTYPE_SUPPLEMENTAL) {
         /* Add any supplemental devices that should be ignored here */
-#define MAKE_TABLE_ENTRY(VID, PID)	((((DWORD)PID)<<16)|VID)
-		static DWORD ignored_devices[] = {
-			MAKE_TABLE_ENTRY(0, 0)
-		};
+#define MAKE_TABLE_ENTRY(VID, PID)    ((((DWORD)PID)<<16)|VID)
+        static DWORD ignored_devices[] = {
+            MAKE_TABLE_ENTRY(0, 0)
+        };
 #undef MAKE_TABLE_ENTRY
-		unsigned int i;
+        unsigned int i;
 
-		for (i = 0; i < SDL_arraysize(ignored_devices); ++i) {
-			if (pdidInstance->guidProduct.Data1 == ignored_devices[i]) {
-				return DIENUM_CONTINUE;
-			}
-		}
+        for (i = 0; i < SDL_arraysize(ignored_devices); ++i) {
+            if (pdidInstance->guidProduct.Data1 == ignored_devices[i]) {
+                return DIENUM_CONTINUE;
+            }
+        }
     }
 
     if (SDL_IsXInputDevice(&pdidInstance->guidProduct)) {
@@ -452,27 +511,38 @@
 
     guid16 = (Uint16 *)pNewJoystick->guid.data;
     if (SDL_memcmp(&pdidInstance->guidProduct.Data4[2], "PIDVID", 6) == 0) {
-        *guid16++ = SDL_SwapLE16(BUS_USB);
+        vendor = (Uint16)LOWORD(pdidInstance->guidProduct.Data1);
+        product = (Uint16)HIWORD(pdidInstance->guidProduct.Data1);
+        version = 0;
+
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
         *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16((Uint16)LOWORD(pdidInstance->guidProduct.Data1)); /* vendor */
+        *guid16++ = SDL_SwapLE16(vendor);
         *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16((Uint16)HIWORD(pdidInstance->guidProduct.Data1)); /* product */
+        *guid16++ = SDL_SwapLE16(product);
         *guid16++ = 0;
-        *guid16++ = 0; /* version */
+        *guid16++ = SDL_SwapLE16(version);
         *guid16++ = 0;
     } else {
-        *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
         *guid16++ = 0;
         SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
     }
 
-    if (SDL_IsGameControllerNameAndGUID(pNewJoystick->joystickname, pNewJoystick->guid) &&
-        SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) {
+    if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
         SDL_free(pNewJoystick);
         return DIENUM_CONTINUE;
     }
 
-    SDL_SYS_AddJoystickDevice(pNewJoystick);
+#ifdef SDL_JOYSTICK_HIDAPI
+    if (HIDAPI_IsDevicePresent(vendor, product, 0)) {
+        /* The HIDAPI driver is taking care of this device */
+        SDL_free(pNewJoystick);
+        return DIENUM_CONTINUE;
+    }
+#endif
+
+    WINDOWS_AddJoystickDevice(pNewJoystick);
 
     return DIENUM_CONTINUE; /* get next device, please */
 }
@@ -683,7 +753,6 @@
 
     /* Force capable? */
     if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
-
         result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
         if (FAILED(result)) {
             return SetDIerror("IDirectInputDevice8::Acquire", result);
@@ -752,6 +821,89 @@
     return 0;
 }
 
+static int
+SDL_DINPUT_JoystickInitRumble(SDL_Joystick * joystick, Sint16 magnitude, Uint32 duration_ms)
+{
+    HRESULT result;
+
+    /* Reset and then enable actuators */
+    result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
+    if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
+        result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
+        if (SUCCEEDED(result)) {
+            result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
+        }
+    }
+    if (FAILED(result)) {
+        return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);
+    }
+
+    result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);
+    if (FAILED(result)) {
+        return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);
+    }
+
+    /* Create the effect */
+    joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
+    if (!joystick->hwdata->ffeffect) {
+        return SDL_OutOfMemory();
+    }
+
+    result = IDirectInputDevice8_CreateEffect(joystick->hwdata->InputDevice, &GUID_Sine,
+                                              joystick->hwdata->ffeffect, &joystick->hwdata->ffeffect_ref, NULL);
+    if (FAILED(result)) {
+        return SetDIerror("IDirectInputDevice8::CreateEffect", result);
+    }
+    return 0;
+}
+
+int
+SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    HRESULT result;
+
+    /* Scale and average the two rumble strengths */
+    Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+
+    if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {
+        return SDL_Unsupported();
+    }
+
+    if (joystick->hwdata->ff_initialized) {
+        DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);
+        joystick->hwdata->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
+        periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+
+        result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
+        if (result == DIERR_INPUTLOST) {
+            result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
+            if (SUCCEEDED(result)) {
+                result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
+            }
+        }
+        if (FAILED(result)) {
+            return SetDIerror("IDirectInputDevice8::SetParameters", result);
+        }
+    } else {
+        if (SDL_DINPUT_JoystickInitRumble(joystick, magnitude, duration_ms) < 0) {
+            return -1;
+        }
+        joystick->hwdata->ff_initialized = SDL_TRUE;
+    }
+
+    result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
+    if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
+        result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
+        if (SUCCEEDED(result)) {
+            result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
+        }
+    }
+    if (FAILED(result)) {
+        return SetDIerror("IDirectInputDevice8::Start", result);
+    }
+    return 0;
+}
+
 static Uint8
 TranslatePOV(DWORD value)
 {
@@ -803,8 +955,6 @@
 
     /* Handle the events or punt */
     if (FAILED(result)) {
-        joystick->hwdata->send_remove_event = SDL_TRUE;
-        joystick->hwdata->removed = SDL_TRUE;
         return;
     }
 
@@ -859,8 +1009,6 @@
     }
 
     if (result != DI_OK) {
-        joystick->hwdata->send_remove_event = SDL_TRUE;
-        joystick->hwdata->removed = SDL_TRUE;
         return;
     }
 
@@ -933,8 +1081,17 @@
 void
 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
 {
+    if (joystick->hwdata->ffeffect_ref) {
+        IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);
+        joystick->hwdata->ffeffect_ref = NULL;
+    }
+    if (joystick->hwdata->ffeffect) {
+        FreeRumbleEffectData(joystick->hwdata->ffeffect);
+        joystick->hwdata->ffeffect = NULL;
+    }
     IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
     IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
+    joystick->hwdata->ff_initialized = SDL_FALSE;
 }
 
 void
@@ -972,6 +1129,12 @@
     return SDL_Unsupported();
 }
 
+int
+SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    return SDL_Unsupported();
+}
+
 void
 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
 {
diff --git a/source/src/joystick/windows/SDL_dinputjoystick_c.h b/source/src/joystick/windows/SDL_dinputjoystick_c.h
index 5cc1890..9f29fc7 100644
--- a/source/src/joystick/windows/SDL_dinputjoystick_c.h
+++ b/source/src/joystick/windows/SDL_dinputjoystick_c.h
@@ -23,6 +23,7 @@
 extern int SDL_DINPUT_JoystickInit(void);
 extern void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext);
 extern int SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice);
+extern int SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 extern void SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick);
 extern void SDL_DINPUT_JoystickClose(SDL_Joystick * joystick);
 extern void SDL_DINPUT_JoystickQuit(void);
diff --git a/source/src/joystick/windows/SDL_mmjoystick.c b/source/src/joystick/windows/SDL_mmjoystick.c
index 9fa8665..60e3fcb 100644
--- a/source/src/joystick/windows/SDL_mmjoystick.c
+++ b/source/src/joystick/windows/SDL_mmjoystick.c
@@ -279,12 +279,6 @@
     return (0);
 }
 
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
-{
-    return SDL_TRUE;
-}
-
 static Uint8
 TranslatePOV(DWORD value)
 {
@@ -366,10 +360,7 @@
 
     /* joystick hat events */
     if (joyinfo.dwFlags & JOY_RETURNPOV) {
-        Uint8 pos;
-
-        pos = TranslatePOV(joyinfo.dwPOV);
-        SDL_PrivateJoystickHat(joystick, 0, pos);
+        SDL_PrivateJoystickHat(joystick, 0, TranslatePOV(joyinfo.dwPOV));
     }
 }
 
diff --git a/source/src/joystick/windows/SDL_windowsjoystick.c b/source/src/joystick/windows/SDL_windowsjoystick.c
index 45cbea6..71b72e6 100644
--- a/source/src/joystick/windows/SDL_windowsjoystick.c
+++ b/source/src/joystick/windows/SDL_windowsjoystick.c
@@ -61,7 +61,6 @@
 /* local variables */
 static SDL_bool s_bDeviceAdded = SDL_FALSE;
 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
-static SDL_JoystickID s_nInstanceID = -1;
 static SDL_cond *s_condJoystickThread = NULL;
 static SDL_mutex *s_mutexJoyStickEnum = NULL;
 static SDL_Thread *s_threadJoystick = NULL;
@@ -256,7 +255,7 @@
             /* WM_DEVICECHANGE not working, no XINPUT, no point in keeping thread alive */
             break;
 #endif /* SDL_JOYSTICK_XINPUT */
-		}
+        }
 
         if (s_bWindowsDeviceChanged || bXInputChanged) {
             s_bDeviceRemoved = SDL_TRUE;
@@ -271,30 +270,33 @@
     return 1;
 }
 
-void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
+void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
 {
     device->send_add_event = SDL_TRUE;
-    device->nInstanceID = ++s_nInstanceID;
+    device->nInstanceID = SDL_GetNextJoystickInstanceID();
     device->pNext = SYS_Joystick;
     SYS_Joystick = device;
 
     s_bDeviceAdded = SDL_TRUE;
 }
 
+static void WINDOWS_JoystickDetect(void);
+static void WINDOWS_JoystickQuit(void);
+
 /* Function to scan the system for joysticks.
  * Joystick 0 should be the system default joystick.
  * It should return 0, or -1 on an unrecoverable fatal error.
  */
-int
-SDL_SYS_JoystickInit(void)
+static int
+WINDOWS_JoystickInit(void)
 {
     if (SDL_DINPUT_JoystickInit() < 0) {
-        SDL_SYS_JoystickQuit();
+        WINDOWS_JoystickQuit();
         return -1;
     }
 
     if (SDL_XINPUT_JoystickInit() < 0) {
-        SDL_SYS_JoystickQuit();
+        WINDOWS_JoystickQuit();
         return -1;
     }
 
@@ -302,19 +304,19 @@
     s_condJoystickThread = SDL_CreateCond();
     s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
 
-    SDL_SYS_JoystickDetect();
+    WINDOWS_JoystickDetect();
 
     if (!s_threadJoystick) {
         /* spin up the thread to detect hotplug of devices */
         s_bJoystickThreadQuit = SDL_FALSE;
         s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
     }
-    return SDL_SYS_NumJoysticks();
+    return 0;
 }
 
 /* return the number of joysticks that are connected right now */
-int
-SDL_SYS_NumJoysticks(void)
+static int
+WINDOWS_JoystickGetCount(void)
 {
     int nJoysticks = 0;
     JoyStick_DeviceData *device = SYS_Joystick;
@@ -327,8 +329,8 @@
 }
 
 /* detect any new joysticks being inserted into the system */
-void
-SDL_SYS_JoystickDetect(void)
+static void
+WINDOWS_JoystickDetect(void)
 {
     JoyStick_DeviceData *pCurList = NULL;
 
@@ -383,7 +385,7 @@
                     SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
                 }
 
-                SDL_PrivateJoystickAdded(device_index);
+                SDL_PrivateJoystickAdded(pNewJoystick->nInstanceID);
 
                 pNewJoystick->send_add_event = SDL_FALSE;
             }
@@ -394,8 +396,8 @@
 }
 
 /* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+WINDOWS_JoystickGetDeviceName(int device_index)
 {
     JoyStick_DeviceData *device = SYS_Joystick;
 
@@ -405,9 +407,34 @@
     return device->joystickname;
 }
 
+static int
+WINDOWS_JoystickGetDevicePlayerIndex(int device_index)
+{
+    JoyStick_DeviceData *device = SYS_Joystick;
+    int index;
+
+    for (index = device_index; index > 0; index--)
+        device = device->pNext;
+
+    return device->bXInputDevice ? (int)device->XInputUserId : -1;
+}
+
+/* return the stable device guid for this device index */
+static SDL_JoystickGUID
+WINDOWS_JoystickGetDeviceGUID(int device_index)
+{
+    JoyStick_DeviceData *device = SYS_Joystick;
+    int index;
+
+    for (index = device_index; index > 0; index--)
+        device = device->pNext;
+
+    return device->guid;
+}
+
 /* Function to perform the mapping between current device instance and this joysticks instance id */
-SDL_JoystickID
-SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickID
+WINDOWS_JoystickGetDeviceInstanceID(int device_index)
 {
     JoyStick_DeviceData *device = SYS_Joystick;
     int index;
@@ -423,8 +450,8 @@
    This should fill the nbuttons and naxes fields of the joystick structure.
    It returns 0, or -1 if there is an error.
  */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index)
 {
     JoyStick_DeviceData *joystickdevice = SYS_Joystick;
 
@@ -448,17 +475,20 @@
     }
 }
 
-/* return true if this joystick is plugged in right now */
-SDL_bool 
-SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
+static int
+WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
 {
-    return joystick->hwdata && !joystick->hwdata->removed;
+    if (joystick->hwdata->bXInputDevice) {
+        return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
+    } else {
+        return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
+    }
 }
 
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static void
+WINDOWS_JoystickUpdate(SDL_Joystick * joystick)
 {
-    if (!joystick->hwdata || joystick->hwdata->removed) {
+    if (!joystick->hwdata) {
         return;
     }
 
@@ -467,15 +497,11 @@
     } else {
         SDL_DINPUT_JoystickUpdate(joystick);
     }
-
-    if (joystick->hwdata->removed) {
-        joystick->force_recentering = SDL_TRUE;
-    }
 }
 
 /* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+WINDOWS_JoystickClose(SDL_Joystick * joystick)
 {
     if (joystick->hwdata->bXInputDevice) {
         SDL_XINPUT_JoystickClose(joystick);
@@ -487,8 +513,8 @@
 }
 
 /* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+WINDOWS_JoystickQuit(void)
 {
     JoyStick_DeviceData *device = SYS_Joystick;
 
@@ -524,24 +550,21 @@
     s_bDeviceRemoved = SDL_FALSE;
 }
 
-/* return the stable device guid for this device index */
-SDL_JoystickGUID
-SDL_SYS_JoystickGetDeviceGUID(int device_index)
+SDL_JoystickDriver SDL_WINDOWS_JoystickDriver =
 {
-    JoyStick_DeviceData *device = SYS_Joystick;
-    int index;
-
-    for (index = device_index; index > 0; index--)
-        device = device->pNext;
-
-    return device->guid;
-}
-
-SDL_JoystickGUID
-SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
-    return joystick->hwdata->guid;
-}
+    WINDOWS_JoystickInit,
+    WINDOWS_JoystickGetCount,
+    WINDOWS_JoystickDetect,
+    WINDOWS_JoystickGetDeviceName,
+    WINDOWS_JoystickGetDevicePlayerIndex,
+    WINDOWS_JoystickGetDeviceGUID,
+    WINDOWS_JoystickGetDeviceInstanceID,
+    WINDOWS_JoystickOpen,
+    WINDOWS_JoystickRumble,
+    WINDOWS_JoystickUpdate,
+    WINDOWS_JoystickClose,
+    WINDOWS_JoystickQuit,
+};
 
 #endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
 
diff --git a/source/src/joystick/windows/SDL_windowsjoystick_c.h b/source/src/joystick/windows/SDL_windowsjoystick_c.h
index 01b8b3a..611f7e6 100644
--- a/source/src/joystick/windows/SDL_windowsjoystick_c.h
+++ b/source/src/joystick/windows/SDL_windowsjoystick_c.h
@@ -66,8 +66,7 @@
 struct joystick_hwdata
 {
     SDL_JoystickGUID guid;
-    SDL_bool removed;
-    SDL_bool send_remove_event;
+    Uint32 rumble_expiration;
 
 #if SDL_JOYSTICK_DINPUT
     LPDIRECTINPUTDEVICE8 InputDevice;
@@ -76,6 +75,9 @@
     input_t Inputs[MAX_INPUTS];
     int NumInputs;
     int NumSliders;
+    SDL_bool ff_initialized;
+    DIEFFECT *ffeffect;
+    LPDIRECTINPUTEFFECT ffeffect_ref;
 #endif
 
     SDL_bool bXInputDevice; /* SDL_TRUE if this device supports using the xinput API rather than DirectInput */
@@ -88,6 +90,6 @@
 extern const DIDATAFORMAT SDL_c_dfDIJoystick2;
 #endif
 
-extern void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device);
+extern void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device);
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/joystick/windows/SDL_xinputjoystick.c b/source/src/joystick/windows/SDL_xinputjoystick.c
index 823e767..6bbe475 100644
--- a/source/src/joystick/windows/SDL_xinputjoystick.c
+++ b/source/src/joystick/windows/SDL_xinputjoystick.c
@@ -26,8 +26,10 @@
 
 #include "SDL_assert.h"
 #include "SDL_hints.h"
+#include "SDL_timer.h"
 #include "SDL_windowsjoystick_c.h"
 #include "SDL_xinputjoystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
 
 /*
  * Internal stuff.
@@ -186,6 +188,9 @@
 static void
 AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
 {
+    Uint16 vendor = 0;
+    Uint16 product = 0;
+    Uint16 version = 0;
     JoyStick_DeviceData *pPrevJoystick = NULL;
     JoyStick_DeviceData *pNewJoystick = *pContext;
 
@@ -229,15 +234,11 @@
     if (SDL_XInputUseOldJoystickMapping()) {
         SDL_zero(pNewJoystick->guid);
     } else {
-        const Uint16 BUS_USB = 0x03;
-        Uint16 vendor = 0;
-        Uint16 product = 0;
-        Uint16 version = 0;
         Uint16 *guid16 = (Uint16 *)pNewJoystick->guid.data;
 
         GuessXInputDevice(userid, &vendor, &product, &version);
 
-        *guid16++ = SDL_SwapLE16(BUS_USB);
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
         *guid16++ = 0;
         *guid16++ = SDL_SwapLE16(vendor);
         *guid16++ = 0;
@@ -253,12 +254,29 @@
     pNewJoystick->SubType = SubType;
     pNewJoystick->XInputUserId = userid;
 
-    if (SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) {
+    if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
         SDL_free(pNewJoystick);
         return;
     }
 
-    SDL_SYS_AddJoystickDevice(pNewJoystick);
+#ifdef SDL_JOYSTICK_HIDAPI
+    if (HIDAPI_IsDevicePresent(vendor, product, version)) {
+        /* The HIDAPI driver is taking care of this device */
+        SDL_free(pNewJoystick);
+        return;
+    }
+#endif
+
+    WINDOWS_AddJoystickDevice(pNewJoystick);
+}
+
+static void
+DelXInputDevice(Uint8 userid)
+{
+    if (s_arrXInputDevicePath[userid]) {
+        SDL_free(s_arrXInputDevicePath[userid]);
+        s_arrXInputDevicePath[userid] = NULL;
+    }
 }
 
 void
@@ -276,6 +294,8 @@
         XINPUT_CAPABILITIES capabilities;
         if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
             AddXInputDevice(userid, capabilities.SubType, pContext);
+        } else {
+            DelXInputDevice(userid);
         }
     }
 }
@@ -291,6 +311,8 @@
     SDL_assert(XINPUTGETCAPABILITIES);
     SDL_assert(XINPUTSETSTATE);
     SDL_assert(userId < XUSER_MAX_COUNT);
+
+    joystick->player_index = userId;
 
     joystick->hwdata->bXInputDevice = SDL_TRUE;
 
@@ -384,12 +406,12 @@
     Uint8 button;
     Uint8 hat = SDL_HAT_CENTERED;
 
-    SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
-    SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
-    SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
-    SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
-    SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
-    SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
+    SDL_PrivateJoystickAxis(joystick, 0, pXInputState->Gamepad.sThumbLX);
+    SDL_PrivateJoystickAxis(joystick, 1, ~pXInputState->Gamepad.sThumbLY);
+    SDL_PrivateJoystickAxis(joystick, 2, ((int)pXInputState->Gamepad.bLeftTrigger * 257) - 32768);
+    SDL_PrivateJoystickAxis(joystick, 3, pXInputState->Gamepad.sThumbRX);
+    SDL_PrivateJoystickAxis(joystick, 4, ~pXInputState->Gamepad.sThumbRY);
+    SDL_PrivateJoystickAxis(joystick, 5, ((int)pXInputState->Gamepad.bRightTrigger * 257) - 32768);
 
     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
@@ -412,6 +434,29 @@
     UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
 }
 
+int
+SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    XINPUT_VIBRATION XVibration;
+
+    if (!XINPUTSETSTATE) {
+        return SDL_Unsupported();
+    }
+
+    XVibration.wLeftMotorSpeed = low_frequency_rumble;
+    XVibration.wRightMotorSpeed = high_frequency_rumble;
+    if (XINPUTSETSTATE(joystick->hwdata->userid, &XVibration) != ERROR_SUCCESS) {
+        return SDL_SetError("XInputSetState() failed");
+    }
+
+    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+        joystick->hwdata->rumble_expiration = SDL_GetTicks() + duration_ms;
+    } else {
+        joystick->hwdata->rumble_expiration = 0;
+    }
+    return 0;
+}
+
 void
 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
 {
@@ -424,14 +469,6 @@
 
     result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState);
     if (result == ERROR_DEVICE_NOT_CONNECTED) {
-        Uint8 userid = joystick->hwdata->userid;
-
-        joystick->hwdata->send_remove_event = SDL_TRUE;
-        joystick->hwdata->removed = SDL_TRUE;
-        if (s_arrXInputDevicePath[userid]) {
-            SDL_free(s_arrXInputDevicePath[userid]);
-            s_arrXInputDevicePath[userid] = NULL;
-        }
         return;
     }
 
@@ -449,6 +486,13 @@
         }
         joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
     }
+
+    if (joystick->hwdata->rumble_expiration) {
+        Uint32 now = SDL_GetTicks();
+        if (SDL_TICKS_PASSED(now, joystick->hwdata->rumble_expiration)) {
+            SDL_XINPUT_JoystickRumble(joystick, 0, 0, 0);
+        }
+    }
 }
 
 void
@@ -462,18 +506,6 @@
     if (s_bXInputEnabled) {
         WIN_UnloadXInputDLL();
     }
-}
-
-SDL_bool
-SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index)
-{
-    JoyStick_DeviceData *device = SYS_Joystick;
-    int index;
-
-    for (index = device_index; index > 0; index--)
-        device = device->pNext;
-
-    return device->bXInputDevice;
 }
 
 #else /* !SDL_JOYSTICK_XINPUT */
@@ -502,6 +534,12 @@
     return SDL_Unsupported();
 }
 
+int
+SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+    return SDL_Unsupported();
+}
+
 void
 SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
 {
diff --git a/source/src/joystick/windows/SDL_xinputjoystick_c.h b/source/src/joystick/windows/SDL_xinputjoystick_c.h
index 63616ee..8d57b62 100644
--- a/source/src/joystick/windows/SDL_xinputjoystick_c.h
+++ b/source/src/joystick/windows/SDL_xinputjoystick_c.h
@@ -26,6 +26,7 @@
 extern int SDL_XINPUT_JoystickInit(void);
 extern void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext);
 extern int SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice);
+extern int SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
 extern void SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick);
 extern void SDL_XINPUT_JoystickClose(SDL_Joystick * joystick);
 extern void SDL_XINPUT_JoystickQuit(void);
diff --git a/source/src/libm/e_exp.c b/source/src/libm/e_exp.c
new file mode 100644
index 0000000..d8cd4a4
--- /dev/null
+++ b/source/src/libm/e_exp.c
@@ -0,0 +1,187 @@
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice
+ * is preserved.
+ * ====================================================
+ */
+
+/* __ieee754_exp(x)
+ * Returns the exponential of x.
+ *
+ * Method
+ *   1. Argument reduction:
+ *      Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658.
+ *	Given x, find r and integer k such that
+ *
+ *               x = k*ln2 + r,  |r| <= 0.5*ln2.
+ *
+ *      Here r will be represented as r = hi-lo for better
+ *	accuracy.
+ *
+ *   2. Approximation of exp(r) by a special rational function on
+ *	the interval [0,0.34658]:
+ *	Write
+ *	    R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ...
+ *      We use a special Reme algorithm on [0,0.34658] to generate
+ * 	a polynomial of degree 5 to approximate R. The maximum error
+ *	of this polynomial approximation is bounded by 2**-59. In
+ *	other words,
+ *	    R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5
+ *  	(where z=r*r, and the values of P1 to P5 are listed below)
+ *	and
+ *	    |                  5          |     -59
+ *	    | 2.0+P1*z+...+P5*z   -  R(z) | <= 2
+ *	    |                             |
+ *	The computation of exp(r) thus becomes
+ *                             2*r
+ *		exp(r) = 1 + -------
+ *		              R - r
+ *                                 r*R1(r)
+ *		       = 1 + r + ----------- (for better accuracy)
+ *		                  2 - R1(r)
+ *	where
+ *			         2       4             10
+ *		R1(r) = r - (P1*r  + P2*r  + ... + P5*r   ).
+ *
+ *   3. Scale back to obtain exp(x):
+ *	From step 1, we have
+ *	   exp(x) = 2^k * exp(r)
+ *
+ * Special cases:
+ *	exp(INF) is INF, exp(NaN) is NaN;
+ *	exp(-INF) is 0, and
+ *	for finite argument, only exp(0)=1 is exact.
+ *
+ * Accuracy:
+ *	according to an error analysis, the error is always less than
+ *	1 ulp (unit in the last place).
+ *
+ * Misc. info.
+ *	For IEEE double
+ *	    if x >  7.09782712893383973096e+02 then exp(x) overflow
+ *	    if x < -7.45133219101941108420e+02 then exp(x) underflow
+ *
+ * Constants:
+ * The hexadecimal values are the intended ones for the following
+ * constants. The decimal values may be used, provided that the
+ * compiler will convert from decimal to binary accurately enough
+ * to produce the hexadecimal values shown.
+ */
+
+#include "math_libm.h"
+#include "math_private.h"
+
+static const double
+one	= 1.0,
+halF[2]	= {0.5,-0.5,},
+huge	= 1.0e+300,
+twom1000= 9.33263618503218878990e-302,     /* 2**-1000=0x01700000,0*/
+o_threshold=  7.09782712893383973096e+02,  /* 0x40862E42, 0xFEFA39EF */
+u_threshold= -7.45133219101941108420e+02,  /* 0xc0874910, 0xD52D3051 */
+ln2HI[2]   ={ 6.93147180369123816490e-01,  /* 0x3fe62e42, 0xfee00000 */
+	     -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */
+ln2LO[2]   ={ 1.90821492927058770002e-10,  /* 0x3dea39ef, 0x35793c76 */
+	     -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */
+invln2 =  1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */
+P1   =  1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */
+P2   = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */
+P3   =  6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */
+P4   = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */
+P5   =  4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */
+
+double __ieee754_exp(double x)	/* default IEEE double exp */
+{
+	double y;
+	double hi = 0.0;
+	double lo = 0.0;
+	double c;
+	double t;
+	int32_t k=0;
+	int32_t xsb;
+	u_int32_t hx;
+
+	GET_HIGH_WORD(hx,x);
+	xsb = (hx>>31)&1;		/* sign bit of x */
+	hx &= 0x7fffffff;		/* high word of |x| */
+
+    /* filter out non-finite argument */
+	if(hx >= 0x40862E42) {			/* if |x|>=709.78... */
+            if(hx>=0x7ff00000) {
+	        u_int32_t lx;
+		GET_LOW_WORD(lx,x);
+		if(((hx&0xfffff)|lx)!=0)
+		     return x+x; 		/* NaN */
+		else return (xsb==0)? x:0.0;	/* exp(+-inf)={inf,0} */
+	    }
+		#if 1
+		if(x > o_threshold) return huge*huge; /* overflow */
+		#else  /* !!! FIXME: check this: "huge * huge" is a compiler warning, maybe they wanted +Inf? */
+		if(x > o_threshold) return INFINITY; /* overflow */
+		#endif
+
+	    if(x < u_threshold) return twom1000*twom1000; /* underflow */
+	}
+
+    /* argument reduction */
+	if(hx > 0x3fd62e42) {		/* if  |x| > 0.5 ln2 */
+	    if(hx < 0x3FF0A2B2) {	/* and |x| < 1.5 ln2 */
+		hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb;
+	    } else {
+		k  = (int32_t) (invln2*x+halF[xsb]);
+		t  = k;
+		hi = x - t*ln2HI[0];	/* t*ln2HI is exact here */
+		lo = t*ln2LO[0];
+	    }
+	    x  = hi - lo;
+	}
+	else if(hx < 0x3e300000)  {	/* when |x|<2**-28 */
+	    if(huge+x>one) return one+x;/* trigger inexact */
+	}
+	else k = 0;
+
+    /* x is now in primary range */
+	t  = x*x;
+	c  = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))));
+	if(k==0) 	return one-((x*c)/(c-2.0)-x);
+	else 		y = one-((lo-(x*c)/(2.0-c))-hi);
+	if(k >= -1021) {
+	    u_int32_t hy;
+	    GET_HIGH_WORD(hy,y);
+	    SET_HIGH_WORD(y,hy+(k<<20));	/* add k to y's exponent */
+	    return y;
+	} else {
+	    u_int32_t hy;
+	    GET_HIGH_WORD(hy,y);
+	    SET_HIGH_WORD(y,hy+((k+1000)<<20));	/* add k to y's exponent */
+	    return y*twom1000;
+	}
+}
+
+/*
+ * wrapper exp(x)
+ */
+#ifndef _IEEE_LIBM
+double exp(double x)
+{
+	static const double o_threshold =  7.09782712893383973096e+02; /* 0x40862E42, 0xFEFA39EF */
+	static const double u_threshold = -7.45133219101941108420e+02; /* 0xc0874910, 0xD52D3051 */
+
+	double z = __ieee754_exp(x);
+	if (_LIB_VERSION == _IEEE_)
+		return z;
+	if (isfinite(x)) {
+		if (x > o_threshold)
+			return __kernel_standard(x, x, 6); /* exp overflow */
+		if (x < u_threshold)
+			return __kernel_standard(x, x, 7); /* exp underflow */
+	}
+	return z;
+}
+#else
+strong_alias(__ieee754_exp, exp)
+#endif
+libm_hidden_def(exp)
diff --git a/source/src/libm/e_rem_pio2.c b/source/src/libm/e_rem_pio2.c
index df7c2b8..5e055d6 100644
--- a/source/src/libm/e_rem_pio2.c
+++ b/source/src/libm/e_rem_pio2.c
@@ -154,7 +154,7 @@
 	}
 	tx[2] = z;
 	nx = 3;
-	while(tx[nx-1]==zero) nx--;	/* skip zero term */
+	while((nx > 0) && tx[nx-1]==zero) nx--;	/* skip zero term */
 	n  =  __kernel_rem_pio2(tx,y,e0,nx,2,two_over_pi);
 	if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;}
 	return n;
diff --git a/source/src/libm/k_rem_pio2.c b/source/src/libm/k_rem_pio2.c
index 7b04275..393db54 100644
--- a/source/src/libm/k_rem_pio2.c
+++ b/source/src/libm/k_rem_pio2.c
@@ -128,6 +128,8 @@
 #include "math_libm.h"
 #include "math_private.h"
 
+#include "SDL_assert.h"
+
 static const int init_jk[] = {2,3,4,6}; /* initial value for jk */
 
 static const double PIo2[] = {
@@ -147,13 +149,19 @@
 two24   =  1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */
 twon24  =  5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */
 
-int attribute_hidden __kernel_rem_pio2(double *x, double *y, int e0, int nx, int prec, const int32_t *ipio2)
+int32_t attribute_hidden __kernel_rem_pio2(double *x, double *y, int e0, int nx, const unsigned int prec, const int32_t *ipio2)
 {
 	int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih;
 	double z,fw,f[20],fq[20],q[20];
 
+	if (nx < 1) {
+		return 0;
+	}
+
     /* initialize jk*/
+	SDL_assert(prec < SDL_arraysize(init_jk));
 	jk = init_jk[prec];
+	SDL_assert(jk > 0);
 	jp = jk;
 
     /* determine jx,jv,q0, note that 3>q0 */
@@ -164,6 +172,9 @@
     /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */
 	j = jv-jx; m = jx+jk;
 	for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (double) ipio2[j];
+	if ((m+1) < SDL_arraysize(f)) {
+        SDL_memset(&f[m+1], 0, sizeof (f) - ((m+1) * sizeof (f[0])));
+    }
 
     /* compute q[0],q[1],...q[jk] */
 	for (i=0;i<=jk;i++) {
@@ -179,6 +190,9 @@
 	    iq[i] =  (int32_t)(z-two24*fw);
 	    z     =  q[j-1]+fw;
 	}
+	if (jz < SDL_arraysize(iq)) {
+        SDL_memset(&iq[jz], 0, sizeof (q) - (jz * sizeof (iq[0])));
+    }
 
     /* compute n */
 	z  = scalbn(z,q0);		/* actual value of z */
@@ -238,7 +252,8 @@
     /* chop off zero terms */
 	if(z==0.0) {
 	    jz -= 1; q0 -= 24;
-	    while(iq[jz]==0) { jz--; q0-=24;}
+		SDL_assert(jz >= 0);
+	    while(iq[jz]==0) { jz--; SDL_assert(jz >= 0); q0-=24;}
 	} else { /* break z into 24-bit if necessary */
 	    z = scalbn(z,-q0);
 	    if(z>=two24) {
@@ -260,6 +275,9 @@
 	    for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k];
 	    fq[jz-i] = fw;
 	}
+	if ((jz+1) < SDL_arraysize(f)) {
+        SDL_memset(&fq[jz+1], 0, sizeof (fq) - ((jz+1) * sizeof (fq[0])));
+    }
 
     /* compress fq[] into y[] */
 	switch(prec) {
diff --git a/source/src/libm/math_libm.h b/source/src/libm/math_libm.h
index eb7bdd5..3c751c5 100644
--- a/source/src/libm/math_libm.h
+++ b/source/src/libm/math_libm.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef math_libm_h_
+#define math_libm_h_
+
 #include "../SDL_internal.h"
 
 /* Math routines from uClibc: http://www.uclibc.org */
@@ -26,6 +30,7 @@
 double SDL_uclibc_atan2(double y, double x);    
 double SDL_uclibc_copysign(double x, double y);       
 double SDL_uclibc_cos(double x);         
+double SDL_uclibc_exp(double x);
 double SDL_uclibc_fabs(double x);        
 double SDL_uclibc_floor(double x);
 double SDL_uclibc_fmod(double x, double y);
@@ -37,4 +42,6 @@
 double SDL_uclibc_sqrt(double x);
 double SDL_uclibc_tan(double x);
 
+#endif /* math_libm_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/libm/math_private.h b/source/src/libm/math_private.h
index 1c0c8a4..d0ef66a 100644
--- a/source/src/libm/math_private.h
+++ b/source/src/libm/math_private.h
@@ -35,6 +35,7 @@
 #define __ieee754_atan2 SDL_uclibc_atan2
 #define copysign        SDL_uclibc_copysign
 #define cos             SDL_uclibc_cos
+#define __ieee754_exp   SDL_uclibc_exp
 #define fabs            SDL_uclibc_fabs
 #define floor           SDL_uclibc_floor
 #define __ieee754_fmod  SDL_uclibc_fmod
@@ -206,7 +207,7 @@
      extern double __ieee754_jn(int, double) attribute_hidden;
      extern double __ieee754_yn(int, double) attribute_hidden;
      extern double __ieee754_remainder(double, double) attribute_hidden;
-     extern int __ieee754_rem_pio2(double, double *) attribute_hidden;
+     extern int32_t __ieee754_rem_pio2(double, double *) attribute_hidden;
 #if defined(_SCALB_INT)
      extern double __ieee754_scalb(double, int) attribute_hidden;
 #else
@@ -220,7 +221,7 @@
      extern double __kernel_sin(double, double, int) attribute_hidden;
      extern double __kernel_cos(double, double) attribute_hidden;
      extern double __kernel_tan(double, double, int) attribute_hidden;
-     extern int __kernel_rem_pio2(double *, double *, int, int, int,
-                                  const int *) attribute_hidden;
+     extern int32_t __kernel_rem_pio2(double *, double *, int, int, const unsigned int,
+                                  const int32_t *) attribute_hidden;
 
 #endif /* _MATH_PRIVATE_H_ */
diff --git a/source/src/main/haiku/SDL_BApp.h b/source/src/main/haiku/SDL_BApp.h
index ba3f927..7adbd00 100644
--- a/source/src/main/haiku/SDL_BApp.h
+++ b/source/src/main/haiku/SDL_BApp.h
@@ -198,7 +198,7 @@
             _current_context->UnlockGL();
         _current_context = newContext;
         if (_current_context)
-	        _current_context->LockGL();
+            _current_context->LockGL();
     }
 #endif
 
@@ -231,7 +231,7 @@
         SDL_SendMouseMotion(win, 0, 0, x, y);
 
         /* Tell the application that the mouse passed over, redraw needed */
-        BE_UpdateWindowFramebuffer(NULL,win,NULL,-1);
+        HAIKU_UpdateWindowFramebuffer(NULL,win,NULL,-1);
     }
 
     void _HandleMouseButton(BMessage *msg) {
@@ -274,11 +274,11 @@
         }
 
         /* Make sure this isn't a repeated event (key pressed and held) */
-        if(state == SDL_PRESSED && BE_GetKeyState(scancode) == SDL_PRESSED) {
+        if(state == SDL_PRESSED && HAIKU_GetKeyState(scancode) == SDL_PRESSED) {
             return;
         }
-        BE_SetKeyState(scancode, state);
-        SDL_SendKeyboardKey(state, BE_GetScancodeFromBeKey(scancode));
+        HAIKU_SetKeyState(scancode, state);
+        SDL_SendKeyboardKey(state, HAIKU_GetScancodeFromBeKey(scancode));
         
         if (state == SDL_PRESSED && SDL_EventState(SDL_TEXTINPUT, SDL_QUERY)) {
             const int8 *keyUtf8;
diff --git a/source/src/main/haiku/SDL_BeApp.cc b/source/src/main/haiku/SDL_BeApp.cc
index f4ee179..cbd2129 100644
--- a/source/src/main/haiku/SDL_BeApp.cc
+++ b/source/src/main/haiku/SDL_BeApp.cc
@@ -31,7 +31,7 @@
 #include <storage/File.h>
 #include <unistd.h>
 
-#include "SDL_BApp.h"	/* SDL_BApp class definition */
+#include "SDL_BApp.h"   /* SDL_BApp class definition */
 #include "SDL_BeApp.h"
 #include "SDL_timer.h"
 #include "SDL_error.h"
@@ -53,24 +53,24 @@
 {
     BApplication *App;
 
-	// default application signature
-	const char *signature = "application/x-SDL-executable";
-	// dig resources for correct signature
-	image_info info;
-	int32 cookie = 0;
-	if (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
-		BFile f(info.name, O_RDONLY);
-		if (f.InitCheck() == B_OK) {
-			BAppFileInfo app_info(&f);
-			if (app_info.InitCheck() == B_OK) {
-				char sig[B_MIME_TYPE_LENGTH];
-				if (app_info.GetSignature(sig) == B_OK)
-					signature = strndup(sig, B_MIME_TYPE_LENGTH);
-			}
-		}
-	}
+    // default application signature
+    const char *signature = "application/x-SDL-executable";
+    // dig resources for correct signature
+    image_info info;
+    int32 cookie = 0;
+    if (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
+        BFile f(info.name, O_RDONLY);
+        if (f.InitCheck() == B_OK) {
+            BAppFileInfo app_info(&f);
+            if (app_info.InitCheck() == B_OK) {
+                char sig[B_MIME_TYPE_LENGTH];
+                if (app_info.GetSignature(sig) == B_OK)
+                    signature = strndup(sig, B_MIME_TYPE_LENGTH);
+            }
+        }
+    }
 
-	App = new SDL_BApp(signature);
+    App = new SDL_BApp(signature);
 
     App->Run();
     delete App;
@@ -144,12 +144,12 @@
 
 /* SDL_BApp functions */
 void SDL_BApp::ClearID(SDL_BWin *bwin) {
-	_SetSDLWindow(NULL, bwin->GetID());
-	int32 i = _GetNumWindowSlots() - 1;
-	while(i >= 0 && GetSDLWindow(i) == NULL) {
-		_PopBackWindow();
-		--i;
-	}
+    _SetSDLWindow(NULL, bwin->GetID());
+    int32 i = _GetNumWindowSlots() - 1;
+    while(i >= 0 && GetSDLWindow(i) == NULL) {
+        _PopBackWindow();
+        --i;
+    }
 }
 
 #endif /* __HAIKU__ */
diff --git a/source/src/main/windows/SDL_windows_main.c b/source/src/main/windows/SDL_windows_main.c
index 5e643a4..32f6727 100644
--- a/source/src/main/windows/SDL_windows_main.c
+++ b/source/src/main/windows/SDL_windows_main.c
@@ -116,50 +116,66 @@
 # endif
 #endif
 
-/* WinMain, main, and wmain eventually call into here. */
-static int
-main_utf8(int argc, char *argv[])
-{
-    SDL_SetMainReady();
-
-    /* Run the application main() code */
-    return SDL_main(argc, argv);
-}
-
 /* Gets the arguments with GetCommandLine, converts them to argc and argv
-   and calls main_utf8 */
+   and calls SDL_main */
 static int
 main_getcmdline()
 {
     char **argv;
     int argc;
-    char *cmdline;
+    char *cmdline = NULL;
     int retval = 0;
+    int cmdalloc = 0;
+    const TCHAR *text = GetCommandLine();
+    const TCHAR *ptr;
+    int argc_guess = 2;  /* space for NULL and initial argument. */
+    int rc;
 
-    /* Grab the command line */
-    TCHAR *text = GetCommandLine();
+    /* make a rough guess of command line arguments. Overestimates if there
+       are quoted things. */
+    for (ptr = text; *ptr; ptr++) {
+        if ((*ptr == ' ') || (*ptr == '\t')) {
+            argc_guess++;
+        }
+    }
+
 #if UNICODE
-    cmdline = WIN_StringToUTF8(text);
+    rc = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
+    if (rc > 0) {
+        cmdalloc = rc + (sizeof (char *) * argc_guess);
+        argv = (char **) VirtualAlloc(NULL, cmdalloc, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+        if (argv) {
+            int rc2;
+            cmdline = (char *) (argv + argc_guess);
+            rc2 = WideCharToMultiByte(CP_UTF8, 0, text, -1, cmdline, rc, NULL, NULL);
+            SDL_assert(rc2 == rc);
+        }
+    }
 #else
     /* !!! FIXME: are these in the system codepage? We need to convert to UTF-8. */
-    cmdline = SDL_strdup(text);
+    rc = ((int) SDL_strlen(text)) + 1;
+    cmdalloc = rc + (sizeof (char *) * argc_guess);
+    argv = (char **) VirtualAlloc(NULL, cmdalloc, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+    if (argv) {
+        cmdline = (char *) (argv + argc_guess);
+        SDL_strcpy(cmdline, text);
+    }
 #endif
     if (cmdline == NULL) {
         return OutOfMemory();
     }
 
     /* Parse it into argv and argc */
-    argc = ParseCommandLine(cmdline, NULL);
-    argv = SDL_stack_alloc(char *, argc + 1);
-    if (argv == NULL) {
-        return OutOfMemory();
-    }
-    ParseCommandLine(cmdline, argv);
+    SDL_assert(ParseCommandLine(cmdline, NULL) <= argc_guess);
+    argc = ParseCommandLine(cmdline, argv);
 
-    retval = main_utf8(argc, argv);
+    SDL_SetMainReady();
 
-    SDL_stack_free(argv);
-    SDL_free(cmdline);
+    /* Run the application main() code */
+    retval = SDL_main(argc, argv);
+
+    VirtualFree(argv, cmdalloc, MEM_DECOMMIT);
+    VirtualFree(argv, 0, MEM_RELEASE);
 
     return retval;
 }
@@ -177,21 +193,7 @@
 int
 console_wmain(int argc, wchar_t *wargv[], wchar_t *wenvp)
 {
-    int retval = 0;
-    char **argv = SDL_stack_alloc(char*, argc + 1);
-    int i;
-
-    for (i = 0; i < argc; ++i) {
-        argv[i] = WIN_StringToUTF8(wargv[i]);
-    }
-    argv[argc] = NULL;
-
-    retval = main_utf8(argc, argv);
-
-    /* !!! FIXME: we are leaking all the elements of argv we allocated. */
-    SDL_stack_free(argv);
-
-    return retval;
+    return main_getcmdline();
 }
 #endif
 
diff --git a/source/src/main/windows/version.rc b/source/src/main/windows/version.rc
index 6f4f1b7..808d111 100644
--- a/source/src/main/windows/version.rc
+++ b/source/src/main/windows/version.rc
@@ -9,8 +9,8 @@
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,0,8,0
- PRODUCTVERSION 2,0,8,0
+ FILEVERSION 2,0,9,0
+ PRODUCTVERSION 2,0,9,0
  FILEFLAGSMASK 0x3fL
  FILEFLAGS 0x0L
  FILEOS 0x40004L
@@ -23,12 +23,12 @@
         BEGIN
             VALUE "CompanyName", "\0"
             VALUE "FileDescription", "SDL\0"
-            VALUE "FileVersion", "2, 0, 8, 0\0"
+            VALUE "FileVersion", "2, 0, 9, 0\0"
             VALUE "InternalName", "SDL\0"
             VALUE "LegalCopyright", "Copyright � 2018 Sam Lantinga\0"
             VALUE "OriginalFilename", "SDL2.dll\0"
             VALUE "ProductName", "Simple DirectMedia Layer\0"
-            VALUE "ProductVersion", "2, 0, 8, 0\0"
+            VALUE "ProductVersion", "2, 0, 9, 0\0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/source/src/power/SDL_power.c b/source/src/power/SDL_power.c
index e09e27b..de77c09 100644
--- a/source/src/power/SDL_power.c
+++ b/source/src/power/SDL_power.c
@@ -42,11 +42,8 @@
     return SDL_TRUE;
 }
 #endif
-#endif
-
 
 static SDL_GetPowerInfo_Impl implementations[] = {
-#ifndef SDL_POWER_DISABLED
 #ifdef SDL_POWER_LINUX          /* in order of preference. More than could work. */
     SDL_GetPowerInfo_Linux_org_freedesktop_upower,
     SDL_GetPowerInfo_Linux_sys_class_power_supply,
@@ -81,31 +78,34 @@
 #ifdef SDL_POWER_HARDWIRED
     SDL_GetPowerInfo_Hardwired,
 #endif
-#endif
 };
+#endif
 
 SDL_PowerState
 SDL_GetPowerInfo(int *seconds, int *percent)
 {
+#ifndef SDL_POWER_DISABLED
     const int total = sizeof(implementations) / sizeof(implementations[0]);
-    int _seconds, _percent;
     SDL_PowerState retval = SDL_POWERSTATE_UNKNOWN;
     int i;
+#endif
 
+    int _seconds, _percent;
     /* Make these never NULL for platform-specific implementations. */
     if (seconds == NULL) {
         seconds = &_seconds;
     }
-
     if (percent == NULL) {
         percent = &_percent;
     }
 
+#ifndef SDL_POWER_DISABLED
     for (i = 0; i < total; i++) {
         if (implementations[i](&retval, seconds, percent)) {
             return retval;
         }
     }
+#endif
 
     /* nothing was definitive. */
     *seconds = -1;
diff --git a/source/src/power/SDL_syspower.h b/source/src/power/SDL_syspower.h
index a9bf70c..afd6268 100644
--- a/source/src/power/SDL_syspower.h
+++ b/source/src/power/SDL_syspower.h
@@ -40,7 +40,9 @@
 SDL_bool SDL_GetPowerInfo_PSP(SDL_PowerState *, int *, int *);
 SDL_bool SDL_GetPowerInfo_WinRT(SDL_PowerState *, int *, int *);
 SDL_bool SDL_GetPowerInfo_Emscripten(SDL_PowerState *, int *, int *);
-SDL_bool SDL_GetPowerInfo_Hardwired(SDL_PowerState *, int *, int *);
+
+/* this one is static in SDL_power.c */
+/* SDL_bool SDL_GetPowerInfo_Hardwired(SDL_PowerState *, int *, int *);*/
 
 #endif /* SDL_syspower_h_ */
 
diff --git a/source/src/power/linux/SDL_syspower.c b/source/src/power/linux/SDL_syspower.c
index 105d5fe..e6c0c1c 100644
--- a/source/src/power/linux/SDL_syspower.c
+++ b/source/src/power/linux/SDL_syspower.c
@@ -608,12 +608,12 @@
 {
     SDL_bool retval = SDL_FALSE;
 
-    #if SDL_USE_LIBDBUS
+#if SDL_USE_LIBDBUS
     SDL_DBusContext *dbus = SDL_DBus_GetContext();
     char **paths = NULL;
     int i, numpaths = 0;
 
-    if (!SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices",
+    if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices",
             DBUS_TYPE_INVALID,
             DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) {
         return SDL_FALSE;  /* try a different approach than UPower. */
@@ -631,7 +631,7 @@
     if (dbus) {
         dbus->free_string_array(paths);
     }
-    #endif  /* SDL_USE_LIBDBUS */
+#endif  /* SDL_USE_LIBDBUS */
 
     return retval;
 }
diff --git a/source/src/render/SDL_render.c b/source/src/render/SDL_render.c
index 8cd3a7b..4985b16 100644
--- a/source/src/render/SDL_render.c
+++ b/source/src/render/SDL_render.c
@@ -132,6 +132,16 @@
 #endif
 }
 
+static void GetWindowViewportValues(SDL_Renderer *renderer, int *logical_w, int *logical_h, SDL_Rect *viewport, SDL_FPoint *scale)
+{
+    SDL_LockMutex(renderer->target_mutex);
+    *logical_w = renderer->target ? renderer->logical_w_backup : renderer->logical_w;
+    *logical_h = renderer->target ? renderer->logical_h_backup : renderer->logical_h;
+    *viewport = renderer->target ? renderer->viewport_backup : renderer->viewport;
+    *scale = renderer->target ? renderer->scale_backup : renderer->scale;
+    SDL_UnlockMutex(renderer->target_mutex);
+}
+
 static int SDLCALL
 SDL_RendererEventWatch(void *userdata, SDL_Event *event)
 {
@@ -197,35 +207,51 @@
         }
     } else if (event->type == SDL_MOUSEMOTION) {
         SDL_Window *window = SDL_GetWindowFromID(event->motion.windowID);
-        if (renderer->logical_w && window == renderer->window) {
-            event->motion.x -= (int)(renderer->viewport.x * renderer->dpi_scale.x);
-            event->motion.y -= (int)(renderer->viewport.y * renderer->dpi_scale.y);
-            event->motion.x = (int)(event->motion.x / (renderer->scale.x * renderer->dpi_scale.x));
-            event->motion.y = (int)(event->motion.y / (renderer->scale.y * renderer->dpi_scale.y));
-            if (event->motion.xrel > 0) {
-                event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (renderer->scale.x * renderer->dpi_scale.x)));
-            } else if (event->motion.xrel < 0) {
-                event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (renderer->scale.x * renderer->dpi_scale.x)));
-            }
-            if (event->motion.yrel > 0) {
-                event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (renderer->scale.y * renderer->dpi_scale.y)));
-            } else if (event->motion.yrel < 0) {
-                event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (renderer->scale.y * renderer->dpi_scale.y)));
+        if (window == renderer->window) {
+            int logical_w, logical_h;
+            SDL_Rect viewport;
+            SDL_FPoint scale;
+            GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
+            if (logical_w) {
+                event->motion.x -= (int)(viewport.x * renderer->dpi_scale.x);
+                event->motion.y -= (int)(viewport.y * renderer->dpi_scale.y);
+                event->motion.x = (int)(event->motion.x / (scale.x * renderer->dpi_scale.x));
+                event->motion.y = (int)(event->motion.y / (scale.y * renderer->dpi_scale.y));
+                if (event->motion.xrel > 0) {
+                    event->motion.xrel = SDL_max(1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
+                } else if (event->motion.xrel < 0) {
+                    event->motion.xrel = SDL_min(-1, (int)(event->motion.xrel / (scale.x * renderer->dpi_scale.x)));
+                }
+                if (event->motion.yrel > 0) {
+                    event->motion.yrel = SDL_max(1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
+                } else if (event->motion.yrel < 0) {
+                    event->motion.yrel = SDL_min(-1, (int)(event->motion.yrel / (scale.y * renderer->dpi_scale.y)));
+                }
             }
         }
     } else if (event->type == SDL_MOUSEBUTTONDOWN ||
                event->type == SDL_MOUSEBUTTONUP) {
         SDL_Window *window = SDL_GetWindowFromID(event->button.windowID);
-        if (renderer->logical_w && window == renderer->window) {
-            event->button.x -= (int)(renderer->viewport.x * renderer->dpi_scale.x);
-            event->button.y -= (int)(renderer->viewport.y * renderer->dpi_scale.y);
-            event->button.x = (int)(event->button.x / (renderer->scale.x * renderer->dpi_scale.x));
-            event->button.y = (int)(event->button.y / (renderer->scale.y * renderer->dpi_scale.y));
+        if (window == renderer->window) {
+            int logical_w, logical_h;
+            SDL_Rect viewport;
+            SDL_FPoint scale;
+            GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
+            if (logical_w) {
+                event->button.x -= (int)(viewport.x * renderer->dpi_scale.x);
+                event->button.y -= (int)(viewport.y * renderer->dpi_scale.y);
+                event->button.x = (int)(event->button.x / (scale.x * renderer->dpi_scale.x));
+                event->button.y = (int)(event->button.y / (scale.y * renderer->dpi_scale.y));
+            }
         }
     } else if (event->type == SDL_FINGERDOWN ||
                event->type == SDL_FINGERUP ||
                event->type == SDL_FINGERMOTION) {
-        if (renderer->logical_w) {
+        int logical_w, logical_h;
+        SDL_Rect viewport;
+        SDL_FPoint scale;
+        GetWindowViewportValues(renderer, &logical_w, &logical_h, &viewport, &scale);
+        if (logical_w) {
             int w = 1;
             int h = 1;
             SDL_GetRendererOutputSize(renderer, &w, &h);
@@ -233,18 +259,18 @@
             event->tfinger.x *= (w - 1);
             event->tfinger.y *= (h - 1);
 
-            event->tfinger.x -= (renderer->viewport.x * renderer->dpi_scale.x);
-            event->tfinger.y -= (renderer->viewport.y * renderer->dpi_scale.y);
-            event->tfinger.x = (event->tfinger.x / (renderer->scale.x * renderer->dpi_scale.x));
-            event->tfinger.y = (event->tfinger.y / (renderer->scale.y * renderer->dpi_scale.y));
+            event->tfinger.x -= (viewport.x * renderer->dpi_scale.x);
+            event->tfinger.y -= (viewport.y * renderer->dpi_scale.y);
+            event->tfinger.x = (event->tfinger.x / (scale.x * renderer->dpi_scale.x));
+            event->tfinger.y = (event->tfinger.y / (scale.y * renderer->dpi_scale.y));
 
-            if (renderer->logical_w > 1) {
-                event->tfinger.x = event->tfinger.x / (renderer->logical_w - 1);
+            if (logical_w > 1) {
+                event->tfinger.x = event->tfinger.x / (logical_w - 1);
             } else {
                 event->tfinger.x = 0.5f;
             }
-            if (renderer->logical_h > 1) {
-                event->tfinger.y = event->tfinger.y / (renderer->logical_h - 1);
+            if (logical_h > 1) {
+                event->tfinger.y = event->tfinger.y / (logical_h - 1);
             } else {
                 event->tfinger.y = 0.5f;
             }
@@ -345,6 +371,7 @@
     if (renderer) {
         renderer->magic = &renderer_magic;
         renderer->window = window;
+        renderer->target_mutex = SDL_CreateMutex();
         renderer->scale.x = 1.0f;
         renderer->scale.y = 1.0f;
         renderer->dpi_scale.x = 1.0f;
@@ -392,6 +419,7 @@
 
     if (renderer) {
         renderer->magic = &renderer_magic;
+        renderer->target_mutex = SDL_CreateMutex();
         renderer->scale.x = 1.0f;
         renderer->scale.y = 1.0f;
 
@@ -493,6 +521,22 @@
     return renderer->info.texture_formats[0];
 }
 
+
+static SDL_ScaleMode SDL_GetScaleMode(void)
+{
+    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
+
+    if (!hint || SDL_strcasecmp(hint, "nearest") == 0) {
+        return SDL_ScaleModeNearest;
+    } else if (SDL_strcasecmp(hint, "linear") == 0) {
+        return SDL_ScaleModeLinear;
+    } else if (SDL_strcasecmp(hint, "best") == 0) {
+        return SDL_ScaleModeBest;
+    } else {
+        return (SDL_ScaleMode)SDL_atoi(hint);
+    }
+}
+
 SDL_Texture *
 SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h)
 {
@@ -534,6 +578,7 @@
     texture->g = 255;
     texture->b = 255;
     texture->a = 255;
+    texture->scaleMode = SDL_GetScaleMode();
     texture->renderer = renderer;
     texture->next = renderer->textures;
     if (renderer->textures) {
@@ -605,7 +650,7 @@
 
     /* See what the best texture format is */
     fmt = surface->format;
-    if (fmt->Amask || SDL_GetColorKey(surface, NULL) == 0) {
+    if (fmt->Amask || SDL_HasColorKey(surface)) {
         needAlpha = SDL_TRUE;
     } else {
         needAlpha = SDL_FALSE;
@@ -664,7 +709,7 @@
         SDL_GetSurfaceAlphaMod(surface, &a);
         SDL_SetTextureAlphaMod(texture, a);
 
-        if (SDL_GetColorKey(surface, NULL) == 0) {
+        if (SDL_HasColorKey(surface)) {
             /* We converted to a texture with alpha format */
             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
         } else {
@@ -1187,6 +1232,8 @@
         }
     }
 
+    SDL_LockMutex(renderer->target_mutex);
+
     if (texture && !renderer->target) {
         /* Make a backup of the viewport */
         renderer->viewport_backup = renderer->viewport;
@@ -1199,6 +1246,7 @@
     renderer->target = texture;
 
     if (renderer->SetRenderTarget(renderer, texture) < 0) {
+        SDL_UnlockMutex(renderer->target_mutex);
         return -1;
     }
 
@@ -1221,6 +1269,9 @@
         renderer->logical_w = renderer->logical_w_backup;
         renderer->logical_h = renderer->logical_h_backup;
     }
+
+    SDL_UnlockMutex(renderer->target_mutex);
+
     if (renderer->UpdateViewport(renderer) < 0) {
         return -1;
     }
@@ -1259,6 +1310,7 @@
 
     hint = SDL_GetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE);
     if (hint && (*hint == '1' || SDL_strcasecmp(hint, "overscan") == 0))  {
+#if SDL_VIDEO_RENDER_D3D
         SDL_bool overscan_supported = SDL_TRUE;
         /* Unfortunately, Direct3D 9 doesn't support negative viewport numbers
            which the overscan implementation relies on.
@@ -1269,6 +1321,9 @@
         if (overscan_supported) {
             scale_policy = 1;
         }
+#else
+        scale_policy = 1;
+#endif
     }
 
     want_aspect = (float)renderer->logical_w / renderer->logical_h;
@@ -2086,6 +2141,10 @@
     /* It's no longer magical... */
     renderer->magic = NULL;
 
+    /* Free the target mutex */
+    SDL_DestroyMutex(renderer->target_mutex);
+    renderer->target_mutex = NULL;
+
     /* Free the renderer instance */
     renderer->DestroyRenderer(renderer);
 }
diff --git a/source/src/render/SDL_sysrender.h b/source/src/render/SDL_sysrender.h
index f0f54c8..940bebc 100644
--- a/source/src/render/SDL_sysrender.h
+++ b/source/src/render/SDL_sysrender.h
@@ -25,11 +25,19 @@
 
 #include "SDL_render.h"
 #include "SDL_events.h"
+#include "SDL_mutex.h"
 #include "SDL_yuv_sw_c.h"
 
 /* The SDL 2D rendering system */
 
 typedef struct SDL_RenderDriver SDL_RenderDriver;
+
+typedef enum
+{
+    SDL_ScaleModeNearest,
+    SDL_ScaleModeLinear,
+    SDL_ScaleModeBest
+} SDL_ScaleMode;
 
 typedef struct
 {
@@ -55,6 +63,7 @@
     int h;                      /**< The height of the texture */
     int modMode;                /**< The texture modulation mode */
     SDL_BlendMode blendMode;    /**< The texture blend mode */
+    SDL_ScaleMode scaleMode;    /**< The texture scale mode */
     Uint8 r, g, b, a;           /**< Texture modulation values */
 
     SDL_Renderer *renderer;
@@ -164,6 +173,7 @@
     /* The list of textures */
     SDL_Texture *textures;
     SDL_Texture *target;
+    SDL_mutex *target_mutex;
 
     Uint8 r, g, b, a;                   /**< Color for drawing operations values */
     SDL_BlendMode blendMode;            /**< The drawing blend mode */
diff --git a/source/src/render/SDL_yuv_sw_c.h b/source/src/render/SDL_yuv_sw_c.h
index 0dfb5db..34322f2 100644
--- a/source/src/render/SDL_yuv_sw_c.h
+++ b/source/src/render/SDL_yuv_sw_c.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_yuv_sw_c_h_
+#define SDL_yuv_sw_c_h_
+
 #include "../SDL_internal.h"
 
 #include "SDL_video.h"
@@ -64,4 +68,6 @@
 #define USE_MMX_ASSEMBLY 1
 #endif
 
+#endif /* SDL_yuv_sw_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/direct3d/SDL_render_d3d.c b/source/src/render/direct3d/SDL_render_d3d.c
index d3be571..69a9dff 100644
--- a/source/src/render/direct3d/SDL_render_d3d.c
+++ b/source/src/render/direct3d/SDL_render_d3d.c
@@ -664,18 +664,6 @@
     return SDL_TRUE;
 }
 
-static D3DTEXTUREFILTERTYPE
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return D3DTEXF_POINT;
-    } else /* if (*hint == '1' || SDL_strcasecmp(hint, "linear") == 0) */ {
-        return D3DTEXF_LINEAR;
-    }
-}
-
 static int
 D3D_CreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD usage, Uint32 format, D3DFORMAT d3dfmt, int w, int h)
 {
@@ -829,7 +817,7 @@
     if (!texturedata) {
         return SDL_OutOfMemory();
     }
-    texturedata->scaleMode = GetScaleQuality();
+    texturedata->scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? D3DTEXF_POINT : D3DTEXF_LINEAR;
 
     texture->driverdata = texturedata;
 
diff --git a/source/src/render/direct3d11/SDL_render_d3d11.c b/source/src/render/direct3d11/SDL_render_d3d11.c
index e0ccfc4..7a37039 100644
--- a/source/src/render/direct3d11/SDL_render_d3d11.c
+++ b/source/src/render/direct3d11/SDL_render_d3d11.c
@@ -1161,17 +1161,6 @@
     return SDL_TRUE;
 }
 
-static D3D11_FILTER
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return D3D11_FILTER_MIN_MAG_MIP_POINT;
-    } else /* if (*hint == '1' || SDL_strcasecmp(hint, "linear") == 0) */ {
-        return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
-    }
-}
-
 static int
 D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
 {
@@ -1192,7 +1181,7 @@
         SDL_OutOfMemory();
         return -1;
     }
-    textureData->scaleMode = GetScaleQuality();
+    textureData->scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ?  D3D11_FILTER_MIN_MAG_MIP_POINT : D3D11_FILTER_MIN_MAG_MIP_LINEAR;
 
     texture->driverdata = textureData;
 
@@ -2234,8 +2223,6 @@
 D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                  const SDL_Rect * srcrect, const SDL_FRect * dstrect)
 {
-    D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata;
-    D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata;
     float minu, maxu, minv, maxv;
     Float4 color;
     VertexPositionColor vertices[4];
@@ -2307,8 +2294,6 @@
                    const SDL_Rect * srcrect, const SDL_FRect * dstrect,
                    const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
 {
-    D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata;
-    D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata;
     float minu, maxu, minv, maxv;
     Float4 color;
     Float4X4 modelMatrix;
diff --git a/source/src/render/metal/SDL_render_metal.m b/source/src/render/metal/SDL_render_metal.m
index f7af72d..5b4d8ea 100644
--- a/source/src/render/metal/SDL_render_metal.m
+++ b/source/src/render/metal/SDL_render_metal.m
@@ -752,8 +752,7 @@
 static void
 METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
 {
-    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
-        event->event == SDL_WINDOWEVENT_SHOWN ||
+    if (event->event == SDL_WINDOWEVENT_SHOWN ||
         event->event == SDL_WINDOWEVENT_HIDDEN) {
         // !!! FIXME: write me
     }
@@ -844,17 +843,24 @@
         mtltexdesc.height = (texture->h + 1) / 2;
         mtltexdesc.textureType = MTLTextureType2DArray;
         mtltexdesc.arrayLength = 2;
-        mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
     } else if (nv12) {
         mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
         mtltexdesc.width = (texture->w + 1) / 2;
         mtltexdesc.height = (texture->h + 1) / 2;
+    }
+
+    if (yuv || nv12) {
         mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
+        if (mtltexture_uv == nil) {
+#if !__has_feature(objc_arc)
+            [mtltexture release];
+#endif
+            return SDL_SetError("Texture allocation failed");
+        }
     }
 
     METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
+    if (texture->scaleMode == SDL_ScaleModeNearest) {
         texturedata.mtlsampler = data.mtlsamplernearest;
     } else {
         texturedata.mtlsampler = data.mtlsamplerlinear;
@@ -957,8 +963,8 @@
                     const Uint8 *Vplane, int Vpitch)
 { @autoreleasepool {
     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
-    int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
-    int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
+    const int Uslice = 0;
+    const int Vslice = 1;
 
     /* Bail out if we're supposed to update an empty rectangle */
     if (rect->w <= 0 || rect->h <= 0) {
@@ -1345,10 +1351,23 @@
 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                     Uint32 pixel_format, void * pixels, int pitch)
 { @autoreleasepool {
+    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
+
+    /* Make sure we have a valid MTLTexture to read from, and an active command
+     * buffer we can wait for. */
     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
 
-    // !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted
-    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
+    /* Wait for the current command buffer to finish, so we don't read from the
+     * texture before the GPU finishes rendering to it. */
+    if (data.mtlcmdencoder) {
+        [data.mtlcmdencoder endEncoding];
+        [data.mtlcmdbuffer commit];
+        [data.mtlcmdbuffer waitUntilCompleted];
+
+        data.mtlcmdencoder = nil;
+        data.mtlcmdbuffer = nil;
+    }
+
     id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
     MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
 
@@ -1364,6 +1383,13 @@
     const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
     const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
     SDL_free(temp_pixels);
+
+    /* Set up an active command buffer and encoder once we're done. It will use
+     * the same texture that was active before (even if it's part of the swap
+     * chain), since we didn't clear that when waiting for the command buffer to
+     * complete. */
+    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
+
     return status;
 }}
 
diff --git a/source/src/render/opengl/SDL_render_gl.c b/source/src/render/opengl/SDL_render_gl.c
index 1c379eb..f3e8326 100644
--- a/source/src/render/opengl/SDL_render_gl.c
+++ b/source/src/render/opengl/SDL_render_gl.c
@@ -703,18 +703,6 @@
     return SDL_TRUE;
 }
 
-static GLenum
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return GL_NEAREST;
-    } else {
-        return GL_LINEAR;
-    }
-}
-
 static int
 GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
 {
@@ -803,7 +791,7 @@
 
     data->format = format;
     data->formattype = type;
-    scaleMode = GetScaleQuality();
+    scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
     renderdata->glEnable(data->type);
     renderdata->glBindTexture(data->type, data->texture);
     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
diff --git a/source/src/render/opengl/SDL_shaders_gl.h b/source/src/render/opengl/SDL_shaders_gl.h
index 9805c59..3697521 100644
--- a/source/src/render/opengl/SDL_shaders_gl.h
+++ b/source/src/render/opengl/SDL_shaders_gl.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_shaders_gl_h_
+#define SDL_shaders_gl_h_
+
 #include "../../SDL_internal.h"
 
 /* OpenGL shader implementation */
@@ -44,4 +48,6 @@
 extern void GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader);
 extern void GL_DestroyShaderContext(GL_ShaderContext *ctx);
 
+#endif /* SDL_shaders_gl_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/opengles/SDL_render_gles.c b/source/src/render/opengles/SDL_render_gles.c
index d6bfca5..4007dff 100644
--- a/source/src/render/opengles/SDL_render_gles.c
+++ b/source/src/render/opengles/SDL_render_gles.c
@@ -524,18 +524,6 @@
     return value;
 }
 
-static GLenum
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return GL_NEAREST;
-    } else {
-        return GL_LINEAR;
-    }
-}
-
 static int
 GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
 {
@@ -603,7 +591,7 @@
 
     data->format = format;
     data->formattype = type;
-    scaleMode = GetScaleQuality();
+    scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
     renderdata->glBindTexture(data->type, data->texture);
     renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
     renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
diff --git a/source/src/render/opengles2/SDL_render_gles2.c b/source/src/render/opengles2/SDL_render_gles2.c
index 0cd388c..fe51b9a 100644
--- a/source/src/render/opengles2/SDL_render_gles2.c
+++ b/source/src/render/opengles2/SDL_render_gles2.c
@@ -553,18 +553,6 @@
 static int GLES2_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
 static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture);
 
-static GLenum
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return GL_NEAREST;
-    } else {
-        return GL_LINEAR;
-    }
-}
-
 static int
 GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
 {
@@ -625,7 +613,7 @@
     data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21));
     data->texture_u = 0;
     data->texture_v = 0;
-    scaleMode = GetScaleQuality();
+    scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;
 
     /* Allocate a blob for image renderdata */
     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
@@ -730,6 +718,10 @@
     Uint8 *src;
     int src_pitch;
     int y;
+
+    if ((width == 0) || (height == 0) || (bpp == 0)) {
+        return 0;  /* nothing to do */
+    }
 
     /* Reformat the texture data into a tightly packed array */
     src_pitch = width * bpp;
@@ -1542,7 +1534,7 @@
     GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
 
 #if !SDL_GLES2_USE_VBOS
-    data->glVertexAttribPointer(attr, attr == GLES2_ATTRIBUTE_ANGLE ? 1 : 2, GL_FLOAT, GL_FALSE, 0, vertexData);
+    data->glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, vertexData);
 #else
     if (!data->vertex_buffers[attr]) {
         data->glGenBuffers(1, &data->vertex_buffers[attr]);
@@ -1557,7 +1549,7 @@
         data->glBufferSubData(GL_ARRAY_BUFFER, 0, dataSizeInBytes, vertexData);
     }
 
-    data->glVertexAttribPointer(attr, attr == GLES2_ATTRIBUTE_ANGLE ? 1 : 2, GL_FLOAT, GL_FALSE, 0, 0);
+    data->glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, 0);
 #endif
 
     return 0;
@@ -1873,8 +1865,9 @@
     GLfloat vertices[8];
     GLfloat texCoords[8];
     GLfloat translate[8];
-    GLfloat fAngle[4];
+    GLfloat fAngle[8];
     GLfloat tmp;
+    float radian_angle;
 
     GLES2_ActivateRenderer(renderer);
 
@@ -1884,7 +1877,11 @@
 
     data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_CENTER);
     data->glEnableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE);
-    fAngle[0] = fAngle[1] = fAngle[2] = fAngle[3] = (GLfloat)(360.0f - angle);
+
+    radian_angle = (float)(M_PI * (360.0 - angle) / 180.0);
+    fAngle[0] = fAngle[2] = fAngle[4] = fAngle[6] = (GLfloat)SDL_sin(radian_angle);
+    /* render expects cos value - 1 (see GLES2_VertexSrc_Default_) */
+    fAngle[1] = fAngle[3] = fAngle[5] = fAngle[7] = (GLfloat)SDL_cos(radian_angle) - 1.0f;
     /* Calculate the center of rotation */
     translate[0] = translate[2] = translate[4] = translate[6] = (center->x + dstrect->x);
     translate[1] = translate[3] = translate[5] = translate[7] = (center->y + dstrect->y);
@@ -1913,7 +1910,7 @@
     data->glVertexAttribPointer(GLES2_ATTRIBUTE_CENTER, 2, GL_FLOAT, GL_FALSE, 0, translate);
     data->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);*/
 
-    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_ANGLE, fAngle, 4 * sizeof(GLfloat));
+    GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_ANGLE, fAngle, 8 * sizeof(GLfloat));
     GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_CENTER, translate, 8 * sizeof(GLfloat));
     GLES2_UpdateVertexBuffer(renderer, GLES2_ATTRIBUTE_POSITION, vertices, 8 * sizeof(GLfloat));
 
@@ -1940,6 +1937,7 @@
 {
     GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
     Uint32 temp_format = renderer->target ? renderer->target->format : SDL_PIXELFORMAT_ABGR8888;
+    size_t buflen;
     void *temp_pixels;
     int temp_pitch;
     Uint8 *src, *dst, *tmp;
@@ -1949,7 +1947,12 @@
     GLES2_ActivateRenderer(renderer);
 
     temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
-    temp_pixels = SDL_malloc(rect->h * temp_pitch);
+    buflen = (size_t) (rect->h * temp_pitch);
+    if (buflen == 0) {
+        return 0;  /* nothing to do. */
+    }
+
+    temp_pixels = SDL_malloc(buflen);
     if (!temp_pixels) {
         return SDL_OutOfMemory();
     }
diff --git a/source/src/render/opengles2/SDL_shaders_gles2.c b/source/src/render/opengles2/SDL_shaders_gles2.c
index b0bcdff..f428a49 100644
--- a/source/src/render/opengles2/SDL_shaders_gles2.c
+++ b/source/src/render/opengles2/SDL_shaders_gles2.c
@@ -30,20 +30,24 @@
 /*************************************************************************************************
  * Vertex/fragment shader source                                                                 *
  *************************************************************************************************/
-
+/* Notes on a_angle:
+   * It is a vector containing sin and cos for rotation matrix
+   * To get correct rotation for most cases when a_angle is disabled cos
+     value is decremented by 1.0 to get proper output with 0.0 which is
+     default value
+*/
 static const Uint8 GLES2_VertexSrc_Default_[] = " \
     uniform mat4 u_projection; \
     attribute vec2 a_position; \
     attribute vec2 a_texCoord; \
-    attribute float a_angle; \
+    attribute vec2 a_angle; \
     attribute vec2 a_center; \
     varying vec2 v_texCoord; \
     \
     void main() \
     { \
-        float angle = radians(a_angle); \
-        float c = cos(angle); \
-        float s = sin(angle); \
+        float s = a_angle[0]; \
+        float c = a_angle[1] + 1.0; \
         mat2 rotationMatrix = mat2(c, -s, s, c); \
         vec2 position = rotationMatrix * (a_position - a_center) + a_center; \
         v_texCoord = a_texCoord; \
diff --git a/source/src/render/psp/SDL_render_psp.c b/source/src/render/psp/SDL_render_psp.c
index 38f893e..babc252 100644
--- a/source/src/render/psp/SDL_render_psp.c
+++ b/source/src/render/psp/SDL_render_psp.c
@@ -187,18 +187,6 @@
 
 
 static int
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return GU_NEAREST; /* GU_NEAREST good for tile-map */
-    } else {
-        return GU_LINEAR; /* GU_LINEAR good for scaling */
-    }
-}
-
-static int
 PixelFormatToPSPFMT(Uint32 format)
 {
     switch (format) {
@@ -514,7 +502,7 @@
 TextureActivate(SDL_Texture * texture)
 {
     PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata;
-    int scaleMode = GetScaleQuality();
+    int scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GU_NEAREST : GU_LINEAR;
 
     /* Swizzling is useless with small textures. */
     if (texture->w >= 16 || texture->h >= 16)
diff --git a/source/src/render/software/SDL_blendfillrect.h b/source/src/render/software/SDL_blendfillrect.h
index 262210f..3cac834 100644
--- a/source/src/render/software/SDL_blendfillrect.h
+++ b/source/src/render/software/SDL_blendfillrect.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_blendfillrect_h_
+#define SDL_blendfillrect_h_
+
 #include "../../SDL_internal.h"
 
 
 extern int SDL_BlendFillRect(SDL_Surface * dst, const SDL_Rect * rect, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 extern int SDL_BlendFillRects(SDL_Surface * dst, const SDL_Rect * rects, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 
+#endif /* SDL_blendfillrect_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/software/SDL_blendline.h b/source/src/render/software/SDL_blendline.h
index 82072cb..a48a498 100644
--- a/source/src/render/software/SDL_blendline.h
+++ b/source/src/render/software/SDL_blendline.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_blendline_h_
+#define SDL_blendline_h_
+
 #include "../../SDL_internal.h"
 
 
 extern int SDL_BlendLine(SDL_Surface * dst, int x1, int y1, int x2, int y2, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 extern int SDL_BlendLines(SDL_Surface * dst, const SDL_Point * points, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 
+#endif /* SDL_blendline_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/software/SDL_blendpoint.h b/source/src/render/software/SDL_blendpoint.h
index dd9e49c..188557c 100644
--- a/source/src/render/software/SDL_blendpoint.h
+++ b/source/src/render/software/SDL_blendpoint.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_blendpoint_h_
+#define SDL_blendpoint_h_
+
 #include "../../SDL_internal.h"
 
 
 extern int SDL_BlendPoint(SDL_Surface * dst, int x, int y, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 extern int SDL_BlendPoints(SDL_Surface * dst, const SDL_Point * points, int count, SDL_BlendMode blendMode, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 
+#endif /* SDL_blendpoint_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/software/SDL_drawline.h b/source/src/render/software/SDL_drawline.h
index 9395d50..4e8e2bd 100644
--- a/source/src/render/software/SDL_drawline.h
+++ b/source/src/render/software/SDL_drawline.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_drawline_h_
+#define SDL_drawline_h_
+
 #include "../../SDL_internal.h"
 
 
 extern int SDL_DrawLine(SDL_Surface * dst, int x1, int y1, int x2, int y2, Uint32 color);
 extern int SDL_DrawLines(SDL_Surface * dst, const SDL_Point * points, int count, Uint32 color);
 
+#endif /* SDL_drawline_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/software/SDL_drawpoint.h b/source/src/render/software/SDL_drawpoint.h
index c366700..454774d 100644
--- a/source/src/render/software/SDL_drawpoint.h
+++ b/source/src/render/software/SDL_drawpoint.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_drawpoint_h_
+#define SDL_drawpoint_h_
+
 #include "../../SDL_internal.h"
 
 
 extern int SDL_DrawPoint(SDL_Surface * dst, int x, int y, Uint32 color);
 extern int SDL_DrawPoints(SDL_Surface * dst, const SDL_Point * points, int count, Uint32 color);
 
+#endif /* SDL_drawpoint_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/software/SDL_render_sw.c b/source/src/render/software/SDL_render_sw.c
index 89e54b8..709dfe8 100644
--- a/source/src/render/software/SDL_render_sw.c
+++ b/source/src/render/software/SDL_render_sw.c
@@ -588,18 +588,6 @@
 }
 
 static int
-GetScaleQuality(void)
-{
-    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
-
-    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-static int
 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
                 const SDL_Rect * srcrect, const SDL_FRect * dstrect,
                 const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
@@ -669,6 +657,11 @@
         blitRequired = SDL_TRUE;
     }
 
+    /* srcrect is not selecting the whole src surface, so cropping is needed */
+    if (!(srcrect->w == src->w && srcrect->h == src->h && srcrect->x == 0 && srcrect->y == 0)) {
+        blitRequired = SDL_TRUE;
+    }
+
     /* The color and alpha modulation has to be applied before the rotation when using the NONE and MOD blend modes. */
     if ((blendmode == SDL_BLENDMODE_NONE || blendmode == SDL_BLENDMODE_MOD) && (alphaMod & rMod & gMod & bMod) != 255) {
         applyModulation = SDL_TRUE;
@@ -717,7 +710,7 @@
 
     if (!retval) {
         SDLgfx_rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, angle, &dstwidth, &dstheight, &cangle, &sangle);
-        src_rotated = SDLgfx_rotateSurface(src_clone, angle, dstwidth/2, dstheight/2, GetScaleQuality(), flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
+        src_rotated = SDLgfx_rotateSurface(src_clone, angle, dstwidth/2, dstheight/2, (texture->scaleMode == SDL_ScaleModeNearest) ? 0 : 1, flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
         if (src_rotated == NULL) {
             retval = -1;
         }
diff --git a/source/src/render/software/SDL_render_sw_c.h b/source/src/render/software/SDL_render_sw_c.h
index 8f065de..f228517 100644
--- a/source/src/render/software/SDL_render_sw_c.h
+++ b/source/src/render/software/SDL_render_sw_c.h
@@ -19,6 +19,11 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_render_sw_c_h_
+#define SDL_render_sw_c_h_
+
 extern SDL_Renderer * SW_CreateRendererForSurface(SDL_Surface * surface);
 
+#endif /* SDL_render_sw_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/render/software/SDL_rotate.c b/source/src/render/software/SDL_rotate.c
index 4476204..09e099c 100644
--- a/source/src/render/software/SDL_rotate.c
+++ b/source/src/render/software/SDL_rotate.c
@@ -83,7 +83,9 @@
 _colorkey(SDL_Surface *src)
 {
     Uint32 key = 0;
-    SDL_GetColorKey(src, &key);
+    if (SDL_HasColorKey(src)) {
+        SDL_GetColorKey(src, &key);
+    }
     return key;
 }
 
@@ -424,8 +426,10 @@
     if (src == NULL)
         return NULL;
 
-    if (SDL_GetColorKey(src, &colorkey) == 0) {
-        colorKeyAvailable = SDL_TRUE;
+    if (SDL_HasColorKey(src)) {
+        if (SDL_GetColorKey(src, &colorkey) == 0) {
+            colorKeyAvailable = SDL_TRUE;
+        }
     }
 
     /* This function requires a 32-bit surface or 8-bit surface with a colorkey */
diff --git a/source/src/render/software/SDL_rotate.h b/source/src/render/software/SDL_rotate.h
index 2bf2ea8..54c0927 100644
--- a/source/src/render/software/SDL_rotate.h
+++ b/source/src/render/software/SDL_rotate.h
@@ -19,6 +19,9 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_rotate_h_
+#define SDL_rotate_h_
+
 #ifndef MIN
 #define MIN(a,b)    (((a) < (b)) ? (a) : (b))
 #endif
@@ -26,3 +29,4 @@
 extern SDL_Surface *SDLgfx_rotateSurface(SDL_Surface * src, double angle, int centerx, int centery, int smooth, int flipx, int flipy, int dstwidth, int dstheight, double cangle, double sangle);
 extern void SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle, int *dstwidth, int *dstheight, double *cangle, double *sangle);
 
+#endif /* SDL_rotate_h_ */
diff --git a/source/src/sensor/SDL_sensor.c b/source/src/sensor/SDL_sensor.c
new file mode 100644
index 0000000..5c7a990
--- /dev/null
+++ b/source/src/sensor/SDL_sensor.c
@@ -0,0 +1,546 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../SDL_internal.h"
+
+/* This is the sensor API for Simple DirectMedia Layer */
+
+#include "SDL.h"
+#include "SDL_atomic.h"
+#include "SDL_events.h"
+#include "SDL_syssensor.h"
+#include "SDL_assert.h"
+
+#if !SDL_EVENTS_DISABLED
+#include "../events/SDL_events_c.h"
+#endif
+
+static SDL_SensorDriver *SDL_sensor_drivers[] = {
+#ifdef SDL_SENSOR_ANDROID
+    &SDL_ANDROID_SensorDriver,
+#endif
+#ifdef SDL_SENSOR_COREMOTION
+    &SDL_COREMOTION_SensorDriver,
+#endif
+#if defined(SDL_SENSOR_DUMMY) || defined(SDL_SENSOR_DISABLED)
+    &SDL_DUMMY_SensorDriver
+#endif
+};
+static SDL_Sensor *SDL_sensors = NULL;
+static SDL_bool SDL_updating_sensor = SDL_FALSE;
+static SDL_mutex *SDL_sensor_lock = NULL; /* This needs to support recursive locks */
+static SDL_atomic_t SDL_next_sensor_instance_id;
+
+static void
+SDL_LockSensors(void)
+{
+    if (SDL_sensor_lock) {
+        SDL_LockMutex(SDL_sensor_lock);
+    }
+}
+
+static void
+SDL_UnlockSensors(void)
+{
+    if (SDL_sensor_lock) {
+        SDL_UnlockMutex(SDL_sensor_lock);
+    }
+}
+
+
+int
+SDL_SensorInit(void)
+{
+    int i, status;
+
+    /* Create the sensor list lock */
+    if (!SDL_sensor_lock) {
+        SDL_sensor_lock = SDL_CreateMutex();
+    }
+
+#if !SDL_EVENTS_DISABLED
+    if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) {
+        return -1;
+    }
+#endif /* !SDL_EVENTS_DISABLED */
+
+    status = -1;
+    for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
+        if (SDL_sensor_drivers[i]->Init() >= 0) {
+            status = 0;
+        }
+    }
+    return status;
+}
+
+/*
+ * Count the number of sensors attached to the system
+ */
+int
+SDL_NumSensors(void)
+{
+    int i, total_sensors = 0;
+    SDL_LockSensors();
+    for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
+        total_sensors += SDL_sensor_drivers[i]->GetCount();
+    }
+    SDL_UnlockSensors();
+    return total_sensors;
+}
+
+/*
+ * Return the next available sensor instance ID
+ * This may be called by drivers from multiple threads, unprotected by any locks
+ */
+SDL_SensorID SDL_GetNextSensorInstanceID()
+{
+    return SDL_AtomicIncRef(&SDL_next_sensor_instance_id);
+}
+
+/*
+ * Get the driver and device index for an API device index
+ * This should be called while the sensor lock is held, to prevent another thread from updating the list
+ */
+static SDL_bool
+SDL_GetDriverAndSensorIndex(int device_index, SDL_SensorDriver **driver, int *driver_index)
+{
+    int i, num_sensors, total_sensors = 0;
+
+    if (device_index >= 0) {
+        for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
+            num_sensors = SDL_sensor_drivers[i]->GetCount();
+            if (device_index < num_sensors) {
+                *driver = SDL_sensor_drivers[i];
+                *driver_index = device_index;
+                return SDL_TRUE;
+            }
+            device_index -= num_sensors;
+            total_sensors += num_sensors;
+        }
+    }
+
+    SDL_SetError("There are %d sensors available", total_sensors);
+    return SDL_FALSE;
+}
+
+/*
+ * Get the implementation dependent name of a sensor
+ */
+const char *
+SDL_SensorGetDeviceName(int device_index)
+{
+    SDL_SensorDriver *driver;
+    const char *name = NULL;
+
+    SDL_LockSensors();
+    if (SDL_GetDriverAndSensorIndex(device_index, &driver, &device_index)) {
+        name = driver->GetDeviceName(device_index);
+    }
+    SDL_UnlockSensors();
+
+    /* FIXME: Really we should reference count this name so it doesn't go away after unlock */
+    return name;
+}
+
+SDL_SensorType
+SDL_SensorGetDeviceType(int device_index)
+{
+    SDL_SensorDriver *driver;
+    SDL_SensorType type = SDL_SENSOR_INVALID;
+
+    SDL_LockSensors();
+    if (SDL_GetDriverAndSensorIndex(device_index, &driver, &device_index)) {
+        type = driver->GetDeviceType(device_index);
+    }
+    SDL_UnlockSensors();
+
+    return type;
+}
+
+SDL_SensorType
+SDL_SensorGetDeviceNonPortableType(int device_index)
+{
+    SDL_SensorDriver *driver;
+    int type = -1;
+
+    SDL_LockSensors();
+    if (SDL_GetDriverAndSensorIndex(device_index, &driver, &device_index)) {
+        type = driver->GetDeviceNonPortableType(device_index);
+    }
+    SDL_UnlockSensors();
+
+    return type;
+}
+
+SDL_SensorID
+SDL_SensorGetDeviceInstanceID(int device_index)
+{
+    SDL_SensorDriver *driver;
+    SDL_SensorID instance_id = -1;
+
+    SDL_LockSensors();
+    if (SDL_GetDriverAndSensorIndex(device_index, &driver, &device_index)) {
+        instance_id = driver->GetDeviceInstanceID(device_index);
+    }
+    SDL_UnlockSensors();
+
+    return instance_id;
+}
+
+/*
+ * Open a sensor for use - the index passed as an argument refers to
+ * the N'th sensor on the system.  This index is the value which will
+ * identify this sensor in future sensor events.
+ *
+ * This function returns a sensor identifier, or NULL if an error occurred.
+ */
+SDL_Sensor *
+SDL_SensorOpen(int device_index)
+{
+    SDL_SensorDriver *driver;
+    SDL_SensorID instance_id;
+    SDL_Sensor *sensor;
+    SDL_Sensor *sensorlist;
+    const char *sensorname = NULL;
+
+    SDL_LockSensors();
+
+    if (!SDL_GetDriverAndSensorIndex(device_index, &driver, &device_index)) {
+        SDL_UnlockSensors();
+        return NULL;
+    }
+
+    sensorlist = SDL_sensors;
+    /* If the sensor is already open, return it
+     * it is important that we have a single sensor * for each instance id
+     */
+    instance_id = driver->GetDeviceInstanceID(device_index);
+    while (sensorlist) {
+        if (instance_id == sensorlist->instance_id) {
+                sensor = sensorlist;
+                ++sensor->ref_count;
+                SDL_UnlockSensors();
+                return sensor;
+        }
+        sensorlist = sensorlist->next;
+    }
+
+    /* Create and initialize the sensor */
+    sensor = (SDL_Sensor *) SDL_calloc(sizeof(*sensor), 1);
+    if (sensor == NULL) {
+        SDL_OutOfMemory();
+        SDL_UnlockSensors();
+        return NULL;
+    }
+    sensor->driver = driver;
+    sensor->instance_id = instance_id;
+    sensor->type = driver->GetDeviceType(device_index);
+    sensor->non_portable_type = driver->GetDeviceNonPortableType(device_index);
+
+    if (driver->Open(sensor, device_index) < 0) {
+        SDL_free(sensor);
+        SDL_UnlockSensors();
+        return NULL;
+    }
+
+    sensorname = driver->GetDeviceName(device_index);
+    if (sensorname) {
+        sensor->name = SDL_strdup(sensorname);
+    } else {
+        sensor->name = NULL;
+    }
+
+    /* Add sensor to list */
+    ++sensor->ref_count;
+    /* Link the sensor in the list */
+    sensor->next = SDL_sensors;
+    SDL_sensors = sensor;
+
+    SDL_UnlockSensors();
+
+    driver->Update(sensor);
+
+    return sensor;
+}
+
+/*
+ * Find the SDL_Sensor that owns this instance id
+ */
+SDL_Sensor *
+SDL_SensorFromInstanceID(SDL_SensorID instance_id)
+{
+    SDL_Sensor *sensor;
+
+    SDL_LockSensors();
+    for (sensor = SDL_sensors; sensor; sensor = sensor->next) {
+        if (sensor->instance_id == instance_id) {
+            break;
+        }
+    }
+    SDL_UnlockSensors();
+    return sensor;
+}
+
+/*
+ * Checks to make sure the sensor is valid.
+ */
+static int
+SDL_PrivateSensorValid(SDL_Sensor * sensor)
+{
+    int valid;
+
+    if (sensor == NULL) {
+        SDL_SetError("Sensor hasn't been opened yet");
+        valid = 0;
+    } else {
+        valid = 1;
+    }
+
+    return valid;
+}
+
+/*
+ * Get the friendly name of this sensor
+ */
+const char *
+SDL_SensorGetName(SDL_Sensor * sensor)
+{
+    if (!SDL_PrivateSensorValid(sensor)) {
+        return NULL;
+    }
+
+    return sensor->name;
+}
+
+/*
+ * Get the type of this sensor
+ */
+SDL_SensorType
+SDL_SensorGetType(SDL_Sensor * sensor)
+{
+    if (!SDL_PrivateSensorValid(sensor)) {
+        return SDL_SENSOR_INVALID;
+    }
+
+    return sensor->type;
+}
+
+/*
+ * Get the platform dependent type of this sensor
+ */
+int
+SDL_SensorGetNonPortableType(SDL_Sensor * sensor)
+{
+    if (!SDL_PrivateSensorValid(sensor)) {
+        return -1;
+    }
+
+    return sensor->non_portable_type;
+}
+
+/*
+ * Get the instance id for this opened sensor
+ */
+SDL_SensorID
+SDL_SensorGetInstanceID(SDL_Sensor * sensor)
+{
+    if (!SDL_PrivateSensorValid(sensor)) {
+        return -1;
+    }
+
+    return sensor->instance_id;
+}
+
+/*
+ * Get the current state of this sensor
+ */
+int
+SDL_SensorGetData(SDL_Sensor * sensor, float *data, int num_values)
+{
+    if (!SDL_PrivateSensorValid(sensor)) {
+        return -1;
+    }
+
+    num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
+    SDL_memcpy(data, sensor->data, num_values*sizeof(*data));
+    return 0;
+}
+
+/*
+ * Close a sensor previously opened with SDL_SensorOpen()
+ */
+void
+SDL_SensorClose(SDL_Sensor * sensor)
+{
+    SDL_Sensor *sensorlist;
+    SDL_Sensor *sensorlistprev;
+
+    if (!SDL_PrivateSensorValid(sensor)) {
+        return;
+    }
+
+    SDL_LockSensors();
+
+    /* First decrement ref count */
+    if (--sensor->ref_count > 0) {
+        SDL_UnlockSensors();
+        return;
+    }
+
+    if (SDL_updating_sensor) {
+        SDL_UnlockSensors();
+        return;
+    }
+
+    sensor->driver->Close(sensor);
+    sensor->hwdata = NULL;
+
+    sensorlist = SDL_sensors;
+    sensorlistprev = NULL;
+    while (sensorlist) {
+        if (sensor == sensorlist) {
+            if (sensorlistprev) {
+                /* unlink this entry */
+                sensorlistprev->next = sensorlist->next;
+            } else {
+                SDL_sensors = sensor->next;
+            }
+            break;
+        }
+        sensorlistprev = sensorlist;
+        sensorlist = sensorlist->next;
+    }
+
+    SDL_free(sensor->name);
+
+    /* Free the data associated with this sensor */
+    SDL_free(sensor);
+
+    SDL_UnlockSensors();
+}
+
+void
+SDL_SensorQuit(void)
+{
+    int i;
+
+    /* Make sure we're not getting called in the middle of updating sensors */
+    SDL_assert(!SDL_updating_sensor);
+
+    SDL_LockSensors();
+
+    /* Stop the event polling */
+    while (SDL_sensors) {
+        SDL_sensors->ref_count = 1;
+        SDL_SensorClose(SDL_sensors);
+    }
+
+    /* Quit the sensor setup */
+    for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
+       SDL_sensor_drivers[i]->Quit();
+    }
+
+    SDL_UnlockSensors();
+
+#if !SDL_EVENTS_DISABLED
+    SDL_QuitSubSystem(SDL_INIT_EVENTS);
+#endif
+
+    if (SDL_sensor_lock) {
+        SDL_DestroyMutex(SDL_sensor_lock);
+        SDL_sensor_lock = NULL;
+    }
+}
+
+
+/* These are global for SDL_syssensor.c and SDL_events.c */
+
+int
+SDL_PrivateSensorUpdate(SDL_Sensor *sensor, float *data, int num_values)
+{
+    int posted;
+
+    /* Allow duplicate events, for things like steps and heartbeats */
+
+    /* Update internal sensor state */
+    num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
+    SDL_memcpy(sensor->data, data, num_values*sizeof(*data));
+
+    /* Post the event, if desired */
+    posted = 0;
+#if !SDL_EVENTS_DISABLED
+    if (SDL_GetEventState(SDL_JOYAXISMOTION) == SDL_ENABLE) {
+        SDL_Event event;
+        event.type = SDL_SENSORUPDATE;
+        event.sensor.which = sensor->instance_id;
+        num_values = SDL_min(num_values, SDL_arraysize(event.sensor.data));
+        SDL_memset(event.sensor.data, 0, sizeof(event.sensor.data));
+        SDL_memcpy(event.sensor.data, data, num_values*sizeof(*data));
+        posted = SDL_PushEvent(&event) == 1;
+    }
+#endif /* !SDL_EVENTS_DISABLED */
+    return posted;
+}
+
+void
+SDL_SensorUpdate(void)
+{
+    int i;
+    SDL_Sensor *sensor;
+
+    SDL_LockSensors();
+
+    if (SDL_updating_sensor) {
+        /* The sensors are already being updated */
+        SDL_UnlockSensors();
+        return;
+    }
+
+    SDL_updating_sensor = SDL_TRUE;
+
+    /* Make sure the list is unlocked while dispatching events to prevent application deadlocks */
+    SDL_UnlockSensors();
+
+    for (sensor = SDL_sensors; sensor; sensor = sensor->next) {
+        sensor->driver->Update(sensor);
+    }
+
+    SDL_LockSensors();
+
+    SDL_updating_sensor = SDL_FALSE;
+
+    /* If any sensors were closed while updating, free them here */
+    for (sensor = SDL_sensors; sensor; sensor = sensor->next) {
+        if (sensor->ref_count <= 0) {
+            SDL_SensorClose(sensor);
+        }
+    }
+
+    /* this needs to happen AFTER walking the sensor list above, so that any
+       dangling hardware data from removed devices can be free'd
+     */
+    for (i = 0; i < SDL_arraysize(SDL_sensor_drivers); ++i) {
+        SDL_sensor_drivers[i]->Detect();
+    }
+
+    SDL_UnlockSensors();
+}
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/SDL_sensor_c.h b/source/src/sensor/SDL_sensor_c.h
new file mode 100644
index 0000000..70974af
--- /dev/null
+++ b/source/src/sensor/SDL_sensor_c.h
@@ -0,0 +1,44 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_sensor_c_h_
+#define SDL_sensor_c_h_
+
+#include "SDL_config.h"
+
+struct _SDL_SensorDriver;
+
+/* Useful functions and variables from SDL_sensor.c */
+#include "SDL_sensor.h"
+
+/* Function to get the next available sensor instance ID */
+extern SDL_SensorID SDL_GetNextSensorInstanceID(void);
+
+/* Initialization and shutdown functions */
+extern int SDL_SensorInit(void);
+extern void SDL_SensorQuit(void);
+
+/* Internal event queueing functions */
+extern int SDL_PrivateSensorUpdate(SDL_Sensor *sensor, float *data, int num_values);
+
+#endif /* SDL_sensor_c_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/SDL_syssensor.h b/source/src/sensor/SDL_syssensor.h
new file mode 100644
index 0000000..210577a
--- /dev/null
+++ b/source/src/sensor/SDL_syssensor.h
@@ -0,0 +1,105 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_syssensor_c_h_
+#define SDL_syssensor_c_h_
+
+#include "SDL_config.h"
+
+/* This is the system specific header for the SDL sensor API */
+
+#include "SDL_sensor.h"
+#include "SDL_sensor_c.h"
+
+/* The SDL sensor structure */
+struct _SDL_Sensor
+{
+    SDL_SensorID instance_id;       /* Device instance, monotonically increasing from 0 */
+    char *name;                     /* Sensor name - system dependent */
+    SDL_SensorType type;            /* Type of the sensor */
+    int non_portable_type;          /* Platform dependent type of the sensor */
+
+    float data[16];                 /* The current state of the sensor */
+
+    struct _SDL_SensorDriver *driver;
+
+    struct sensor_hwdata *hwdata;   /* Driver dependent information */
+
+    int ref_count;                  /* Reference count for multiple opens */
+
+    struct _SDL_Sensor *next;       /* pointer to next sensor we have allocated */
+};
+
+typedef struct _SDL_SensorDriver
+{
+    /* Function to scan the system for sensors.
+     * sensor 0 should be the system default sensor.
+     * This function should return 0, or -1 on an unrecoverable fatal error.
+     */
+    int (*Init)(void);
+
+    /* Function to return the number of sensors available right now */
+    int (*GetCount)(void);
+
+    /* Function to check to see if the available sensors have changed */
+    void (*Detect)(void);
+
+    /* Function to get the device-dependent name of a sensor */
+    const char *(*GetDeviceName)(int device_index);
+
+    /* Function to get the type of a sensor */
+    SDL_SensorType (*GetDeviceType)(int device_index);
+
+    /* Function to get the platform dependent type of a sensor */
+    int (*GetDeviceNonPortableType)(int device_index);
+
+    /* Function to get the current instance id of the sensor located at device_index */
+    SDL_SensorID (*GetDeviceInstanceID)(int device_index);
+
+    /* Function to open a sensor for use.
+       The sensor to open is specified by the device index.
+       It returns 0, or -1 if there is an error.
+     */
+    int (*Open)(SDL_Sensor * sensor, int device_index);
+
+    /* Function to update the state of a sensor - called as a device poll.
+     * This function shouldn't update the sensor structure directly,
+     * but instead should call SDL_PrivateSensorUpdate() to deliver events
+     * and update sensor device state.
+     */
+    void (*Update)(SDL_Sensor * sensor);
+
+    /* Function to close a sensor after use */
+    void (*Close)(SDL_Sensor * sensor);
+
+    /* Function to perform any system-specific sensor related cleanup */
+    void (*Quit)(void);
+
+} SDL_SensorDriver;
+
+/* The available sensor drivers */
+extern SDL_SensorDriver SDL_ANDROID_SensorDriver;
+extern SDL_SensorDriver SDL_COREMOTION_SensorDriver;
+extern SDL_SensorDriver SDL_DUMMY_SensorDriver;
+
+#endif /* SDL_syssensor_c_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/android/SDL_androidsensor.c b/source/src/sensor/android/SDL_androidsensor.c
new file mode 100644
index 0000000..117c18d
--- /dev/null
+++ b/source/src/sensor/android/SDL_androidsensor.c
@@ -0,0 +1,211 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_config.h"
+
+#ifdef SDL_SENSOR_ANDROID
+
+/* This is the system specific header for the SDL sensor API */
+#include <android/sensor.h>
+
+#include "SDL_error.h"
+#include "SDL_sensor.h"
+#include "SDL_androidsensor.h"
+#include "../SDL_syssensor.h"
+#include "../SDL_sensor_c.h"
+//#include "../../core/android/SDL_android.h"
+
+#ifndef LOOPER_ID_USER
+#define LOOPER_ID_USER  3
+#endif
+
+typedef struct
+{
+    ASensorRef asensor;
+    SDL_SensorID instance_id;
+} SDL_AndroidSensor;
+
+static ASensorManager* SDL_sensor_manager;
+static ALooper* SDL_sensor_looper;
+static SDL_AndroidSensor *SDL_sensors;
+static int SDL_sensors_count;
+
+static int
+SDL_ANDROID_SensorInit(void)
+{
+    int i, sensors_count;
+    ASensorList sensors;
+
+    SDL_sensor_manager = ASensorManager_getInstance();
+    if (!SDL_sensor_manager) {
+        return SDL_SetError("Couldn't create sensor manager");
+    }
+
+    SDL_sensor_looper = ALooper_forThread();
+    if (!SDL_sensor_looper) {
+        SDL_sensor_looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+        if (!SDL_sensor_looper) {
+            return SDL_SetError("Couldn't create sensor event loop");
+        }
+    }
+
+    /* FIXME: Is the sensor list dynamic? */
+    sensors_count = ASensorManager_getSensorList(SDL_sensor_manager, &sensors);
+    if (sensors_count > 0) {
+        SDL_sensors = (SDL_AndroidSensor *)SDL_calloc(sensors_count, sizeof(*SDL_sensors));
+        if (!SDL_sensors) {
+            return SDL_OutOfMemory();
+        }
+
+        for (i = 0; i < sensors_count; ++i) {
+            SDL_sensors[i].asensor = sensors[i];
+            SDL_sensors[i].instance_id = SDL_GetNextSensorInstanceID();
+        }
+        SDL_sensors_count = sensors_count;
+    }
+    return 0;
+}
+
+static int
+SDL_ANDROID_SensorGetCount(void)
+{
+    return SDL_sensors_count;
+}
+
+static void
+SDL_ANDROID_SensorDetect(void)
+{
+}
+
+static const char *
+SDL_ANDROID_SensorGetDeviceName(int device_index)
+{
+    return ASensor_getName(SDL_sensors[device_index].asensor);
+}
+
+static SDL_SensorType
+SDL_ANDROID_SensorGetDeviceType(int device_index)
+{
+    switch (ASensor_getType(SDL_sensors[device_index].asensor)) {
+    case 0x00000001:
+        return SDL_SENSOR_ACCEL;
+    case 0x00000004:
+        return SDL_SENSOR_GYRO;
+    default:
+        return SDL_SENSOR_UNKNOWN;
+    }
+}
+
+static int
+SDL_ANDROID_SensorGetDeviceNonPortableType(int device_index)
+{
+    return ASensor_getType(SDL_sensors[device_index].asensor);
+}
+
+static SDL_SensorID
+SDL_ANDROID_SensorGetDeviceInstanceID(int device_index)
+{
+    return SDL_sensors[device_index].instance_id;
+}
+
+static int
+SDL_ANDROID_SensorOpen(SDL_Sensor *sensor, int device_index)
+{
+    struct sensor_hwdata *hwdata;
+
+    hwdata = (struct sensor_hwdata *)SDL_calloc(1, sizeof(*hwdata));
+    if (hwdata == NULL) {
+        return SDL_OutOfMemory();
+    }
+
+    hwdata->asensor = SDL_sensors[device_index].asensor;
+    hwdata->eventqueue = ASensorManager_createEventQueue(SDL_sensor_manager, SDL_sensor_looper, LOOPER_ID_USER, NULL, NULL);
+    if (!hwdata->eventqueue) {
+        SDL_free(hwdata);
+        return SDL_SetError("Couldn't create sensor event queue");
+    }
+
+    if (ASensorEventQueue_enableSensor(hwdata->eventqueue, hwdata->asensor) < 0) {
+        ASensorManager_destroyEventQueue(SDL_sensor_manager, hwdata->eventqueue);
+        SDL_free(hwdata);
+        return SDL_SetError("Couldn't enable sensor");
+    }
+
+    /* FIXME: What rate should we set for this sensor? 60 FPS? Let's try the default rate for now... */
+
+    sensor->hwdata = hwdata;
+    return 0;
+}
+    
+static void
+SDL_ANDROID_SensorUpdate(SDL_Sensor *sensor)
+{
+    int events;
+    ASensorEvent event;
+    struct android_poll_source* source;
+
+    if (ALooper_pollAll(0, NULL, &events, (void**)&source) == LOOPER_ID_USER) {
+        SDL_zero(event);
+        while (ASensorEventQueue_getEvents(sensor->hwdata->eventqueue, &event, 1) > 0) {
+            SDL_PrivateSensorUpdate(sensor, event.data, SDL_arraysize(event.data));
+        }
+    }
+}
+
+static void
+SDL_ANDROID_SensorClose(SDL_Sensor *sensor)
+{
+    if (sensor->hwdata) {
+        ASensorEventQueue_disableSensor(sensor->hwdata->eventqueue, sensor->hwdata->asensor);
+        ASensorManager_destroyEventQueue(SDL_sensor_manager, sensor->hwdata->eventqueue);
+        SDL_free(sensor->hwdata);
+        sensor->hwdata = NULL;
+    }
+}
+
+static void
+SDL_ANDROID_SensorQuit(void)
+{
+    if (SDL_sensors) {
+        SDL_free(SDL_sensors);
+        SDL_sensors = NULL;
+        SDL_sensors_count = 0;
+    }
+}
+
+SDL_SensorDriver SDL_ANDROID_SensorDriver =
+{
+    SDL_ANDROID_SensorInit,
+    SDL_ANDROID_SensorGetCount,
+    SDL_ANDROID_SensorDetect,
+    SDL_ANDROID_SensorGetDeviceName,
+    SDL_ANDROID_SensorGetDeviceType,
+    SDL_ANDROID_SensorGetDeviceNonPortableType,
+    SDL_ANDROID_SensorGetDeviceInstanceID,
+    SDL_ANDROID_SensorOpen,
+    SDL_ANDROID_SensorUpdate,
+    SDL_ANDROID_SensorClose,
+    SDL_ANDROID_SensorQuit,
+};
+
+#endif /* SDL_SENSOR_ANDROID */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/android/SDL_androidsensor.h b/source/src/sensor/android/SDL_androidsensor.h
new file mode 100644
index 0000000..c65002e
--- /dev/null
+++ b/source/src/sensor/android/SDL_androidsensor.h
@@ -0,0 +1,31 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_config.h"
+
+/* The private structure used to keep track of a sensor */
+struct sensor_hwdata
+{
+    ASensorRef asensor;
+    ASensorEventQueue *eventqueue;
+};
+
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/coremotion/SDL_coremotionsensor.h b/source/src/sensor/coremotion/SDL_coremotionsensor.h
new file mode 100644
index 0000000..2312e84
--- /dev/null
+++ b/source/src/sensor/coremotion/SDL_coremotionsensor.h
@@ -0,0 +1,30 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_config.h"
+
+/* The private structure used to keep track of a sensor */
+struct sensor_hwdata
+{
+    float data[3];
+};
+
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/coremotion/SDL_coremotionsensor.m b/source/src/sensor/coremotion/SDL_coremotionsensor.m
new file mode 100644
index 0000000..526cce8
--- /dev/null
+++ b/source/src/sensor/coremotion/SDL_coremotionsensor.m
@@ -0,0 +1,234 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_config.h"
+
+#ifdef SDL_SENSOR_COREMOTION
+
+/* This is the system specific header for the SDL sensor API */
+#include <CoreMotion/CoreMotion.h>
+
+#include "SDL_error.h"
+#include "SDL_sensor.h"
+#include "SDL_coremotionsensor.h"
+#include "../SDL_syssensor.h"
+#include "../SDL_sensor_c.h"
+
+typedef struct
+{
+    SDL_SensorType type;
+    SDL_SensorID instance_id;
+} SDL_CoreMotionSensor;
+
+static CMMotionManager *SDL_motion_manager;
+static SDL_CoreMotionSensor *SDL_sensors;
+static int SDL_sensors_count;
+
+static int
+SDL_COREMOTION_SensorInit(void)
+{
+    int i, sensors_count = 0;
+
+    if (!SDL_motion_manager) {
+        SDL_motion_manager = [[CMMotionManager alloc] init];
+    }
+
+    if (SDL_motion_manager.accelerometerAvailable) {
+        ++sensors_count;
+    }
+    if (SDL_motion_manager.gyroAvailable) {
+        ++sensors_count;
+    }
+
+    if (sensors_count > 0) {
+        SDL_sensors = (SDL_CoreMotionSensor *)SDL_calloc(sensors_count, sizeof(*SDL_sensors));
+        if (!SDL_sensors) {
+            return SDL_OutOfMemory();
+        }
+
+        i = 0;
+        if (SDL_motion_manager.accelerometerAvailable) {
+            SDL_sensors[i].type = SDL_SENSOR_ACCEL;
+            SDL_sensors[i].instance_id = SDL_GetNextSensorInstanceID();
+            ++i;
+        }
+        if (SDL_motion_manager.gyroAvailable) {
+            SDL_sensors[i].type = SDL_SENSOR_GYRO;
+            SDL_sensors[i].instance_id = SDL_GetNextSensorInstanceID();
+            ++i;
+        }
+        SDL_sensors_count = sensors_count;
+    }
+    return 0;
+}
+
+static int
+SDL_COREMOTION_SensorGetCount(void)
+{
+    return SDL_sensors_count;
+}
+
+static void
+SDL_COREMOTION_SensorDetect(void)
+{
+}
+
+static const char *
+SDL_COREMOTION_SensorGetDeviceName(int device_index)
+{
+    switch (SDL_sensors[device_index].type) {
+    case SDL_SENSOR_ACCEL:
+        return "Accelerometer";
+    case SDL_SENSOR_GYRO:
+        return "Gyro";
+    default:
+        return "Unknown";
+    }
+}
+
+static SDL_SensorType
+SDL_COREMOTION_SensorGetDeviceType(int device_index)
+{
+    return SDL_sensors[device_index].type;
+}
+
+static int
+SDL_COREMOTION_SensorGetDeviceNonPortableType(int device_index)
+{
+    return SDL_sensors[device_index].type;
+}
+
+static SDL_SensorID
+SDL_COREMOTION_SensorGetDeviceInstanceID(int device_index)
+{
+    return SDL_sensors[device_index].instance_id;
+}
+
+static int
+SDL_COREMOTION_SensorOpen(SDL_Sensor *sensor, int device_index)
+{
+    struct sensor_hwdata *hwdata;
+
+    hwdata = (struct sensor_hwdata *)SDL_calloc(1, sizeof(*hwdata));
+    if (hwdata == NULL) {
+        return SDL_OutOfMemory();
+    }
+    sensor->hwdata = hwdata;
+
+    switch (sensor->type)
+    {
+    case SDL_SENSOR_ACCEL:
+        [SDL_motion_manager startAccelerometerUpdates];
+        break;
+    case SDL_SENSOR_GYRO:
+        [SDL_motion_manager startGyroUpdates];
+        break;
+    default:
+        break;
+    }
+    return 0;
+}
+    
+static void
+SDL_COREMOTION_SensorUpdate(SDL_Sensor *sensor)
+{
+    switch (sensor->type)
+    {
+    case SDL_SENSOR_ACCEL:
+        {
+            CMAccelerometerData *accelerometerData = SDL_motion_manager.accelerometerData;
+            if (accelerometerData) {
+                CMAcceleration acceleration = accelerometerData.acceleration;
+                float data[3];
+                data[0] = acceleration.x * SDL_STANDARD_GRAVITY;
+                data[1] = acceleration.y * SDL_STANDARD_GRAVITY;
+                data[2] = acceleration.z * SDL_STANDARD_GRAVITY;
+                if (SDL_memcmp(data, sensor->hwdata->data, sizeof(data)) != 0) {
+                    SDL_PrivateSensorUpdate(sensor, data, SDL_arraysize(data));
+                    SDL_memcpy(sensor->hwdata->data, data, sizeof(data));
+                }
+            }
+        }
+        break;
+    case SDL_SENSOR_GYRO:
+        {
+            CMGyroData *gyroData = SDL_motion_manager.gyroData;
+            if (gyroData) {
+                CMRotationRate rotationRate = gyroData.rotationRate;
+                float data[3];
+                data[0] = rotationRate.x;
+                data[1] = rotationRate.y;
+                data[2] = rotationRate.z;
+                if (SDL_memcmp(data, sensor->hwdata->data, sizeof(data)) != 0) {
+                    SDL_PrivateSensorUpdate(sensor, data, SDL_arraysize(data));
+                    SDL_memcpy(sensor->hwdata->data, data, sizeof(data));
+                }
+            }
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+static void
+SDL_COREMOTION_SensorClose(SDL_Sensor *sensor)
+{
+    if (sensor->hwdata) {
+        switch (sensor->type)
+        {
+        case SDL_SENSOR_ACCEL:
+            [SDL_motion_manager stopAccelerometerUpdates];
+            break;
+        case SDL_SENSOR_GYRO:
+            [SDL_motion_manager stopGyroUpdates];
+            break;
+        default:
+            break;
+        }
+        SDL_free(sensor->hwdata);
+        sensor->hwdata = NULL;
+    }
+}
+
+static void
+SDL_COREMOTION_SensorQuit(void)
+{
+}
+
+SDL_SensorDriver SDL_COREMOTION_SensorDriver =
+{
+    SDL_COREMOTION_SensorInit,
+    SDL_COREMOTION_SensorGetCount,
+    SDL_COREMOTION_SensorDetect,
+    SDL_COREMOTION_SensorGetDeviceName,
+    SDL_COREMOTION_SensorGetDeviceType,
+    SDL_COREMOTION_SensorGetDeviceNonPortableType,
+    SDL_COREMOTION_SensorGetDeviceInstanceID,
+    SDL_COREMOTION_SensorOpen,
+    SDL_COREMOTION_SensorUpdate,
+    SDL_COREMOTION_SensorClose,
+    SDL_COREMOTION_SensorQuit,
+};
+
+#endif /* SDL_SENSOR_COREMOTION */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/dummy/SDL_dummysensor.c b/source/src/sensor/dummy/SDL_dummysensor.c
new file mode 100644
index 0000000..cf04045
--- /dev/null
+++ b/source/src/sensor/dummy/SDL_dummysensor.c
@@ -0,0 +1,110 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_config.h"
+
+#if defined(SDL_SENSOR_DUMMY) || defined(SDL_SENSOR_DISABLED)
+
+#include "SDL_error.h"
+#include "SDL_sensor.h"
+#include "SDL_dummysensor.h"
+#include "../SDL_syssensor.h"
+
+static int
+SDL_DUMMY_SensorInit(void)
+{
+    return 0;
+}
+
+static int
+SDL_DUMMY_SensorGetCount(void)
+{
+    return 0;
+}
+
+static void
+SDL_DUMMY_SensorDetect(void)
+{
+}
+
+static const char *
+SDL_DUMMY_SensorGetDeviceName(int device_index)
+{
+    return NULL;
+}
+
+static SDL_SensorType
+SDL_DUMMY_SensorGetDeviceType(int device_index)
+{
+    return SDL_SENSOR_INVALID;
+}
+
+static int
+SDL_DUMMY_SensorGetDeviceNonPortableType(int device_index)
+{
+    return -1;
+}
+
+static SDL_SensorID
+SDL_DUMMY_SensorGetDeviceInstanceID(int device_index)
+{
+    return -1;
+}
+
+static int
+SDL_DUMMY_SensorOpen(SDL_Sensor *sensor, int device_index)
+{
+    return SDL_Unsupported();
+}
+    
+static void
+SDL_DUMMY_SensorUpdate(SDL_Sensor *sensor)
+{
+}
+
+static void
+SDL_DUMMY_SensorClose(SDL_Sensor *sensor)
+{
+}
+
+static void
+SDL_DUMMY_SensorQuit(void)
+{
+}
+
+SDL_SensorDriver SDL_DUMMY_SensorDriver =
+{
+    SDL_DUMMY_SensorInit,
+    SDL_DUMMY_SensorGetCount,
+    SDL_DUMMY_SensorDetect,
+    SDL_DUMMY_SensorGetDeviceName,
+    SDL_DUMMY_SensorGetDeviceType,
+    SDL_DUMMY_SensorGetDeviceNonPortableType,
+    SDL_DUMMY_SensorGetDeviceInstanceID,
+    SDL_DUMMY_SensorOpen,
+    SDL_DUMMY_SensorUpdate,
+    SDL_DUMMY_SensorClose,
+    SDL_DUMMY_SensorQuit,
+};
+
+#endif /* SDL_SENSOR_DUMMY || SDL_SENSOR_DISABLED */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/sensor/dummy/SDL_dummysensor.h b/source/src/sensor/dummy/SDL_dummysensor.h
new file mode 100644
index 0000000..507ee93
--- /dev/null
+++ b/source/src/sensor/dummy/SDL_dummysensor.h
@@ -0,0 +1,23 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_config.h"
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/stdlib/SDL_iconv.c b/source/src/stdlib/SDL_iconv.c
index f8dbc9f..e2e3a3f 100644
--- a/source/src/stdlib/SDL_iconv.c
+++ b/source/src/stdlib/SDL_iconv.c
@@ -89,13 +89,13 @@
 #else
 
 /* Lots of useful information on Unicode at:
-	http://www.cl.cam.ac.uk/~mgk25/unicode.html
+    http://www.cl.cam.ac.uk/~mgk25/unicode.html
 */
 
-#define UNICODE_BOM	0xFEFF
+#define UNICODE_BOM    0xFEFF
 
-#define UNKNOWN_ASCII	'?'
-#define UNKNOWN_UNICODE	0xFFFD
+#define UNKNOWN_ASCII    '?'
+#define UNKNOWN_UNICODE    0xFFFD
 
 enum
 {
@@ -115,13 +115,13 @@
     ENCODING_UCS4LE,
 };
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-#define ENCODING_UTF16NATIVE	ENCODING_UTF16BE
-#define ENCODING_UTF32NATIVE	ENCODING_UTF32BE
+#define ENCODING_UTF16NATIVE    ENCODING_UTF16BE
+#define ENCODING_UTF32NATIVE    ENCODING_UTF32BE
 #define ENCODING_UCS2NATIVE     ENCODING_UCS2BE
 #define ENCODING_UCS4NATIVE     ENCODING_UCS4BE
 #else
-#define ENCODING_UTF16NATIVE	ENCODING_UTF16LE
-#define ENCODING_UTF32NATIVE	ENCODING_UTF32LE
+#define ENCODING_UTF16NATIVE    ENCODING_UTF16LE
+#define ENCODING_UTF32NATIVE    ENCODING_UTF32LE
 #define ENCODING_UCS2NATIVE     ENCODING_UCS2LE
 #define ENCODING_UCS4NATIVE     ENCODING_UCS4LE
 #endif
diff --git a/source/src/stdlib/SDL_stdlib.c b/source/src/stdlib/SDL_stdlib.c
index b36d83c..d500bf4 100644
--- a/source/src/stdlib/SDL_stdlib.c
+++ b/source/src/stdlib/SDL_stdlib.c
@@ -201,10 +201,30 @@
 }
 
 double
+SDL_exp(double x)
+{
+#if defined(HAVE_EXP)
+    return exp(x);
+#else
+    return SDL_uclibc_exp(x);
+#endif
+}
+
+float
+SDL_expf(float x)
+{
+#if defined(HAVE_EXPF)
+    return expf(x);
+#else
+    return (float)SDL_exp((double)x);
+#endif
+}
+
+double
 SDL_fabs(double x)
 {
 #if defined(HAVE_FABS)
-    return fabs(x); 
+    return fabs(x);
 #else
     return SDL_uclibc_fabs(x);
 #endif
@@ -214,7 +234,7 @@
 SDL_fabsf(float x)
 {
 #if defined(HAVE_FABSF)
-    return fabsf(x); 
+    return fabsf(x);
 #else
     return (float)SDL_fabs((double)x);
 #endif
diff --git a/source/src/stdlib/SDL_string.c b/source/src/stdlib/SDL_string.c
index 444ae6d..a563adf 100644
--- a/source/src/stdlib/SDL_string.c
+++ b/source/src/stdlib/SDL_string.c
@@ -271,12 +271,16 @@
     size_t left;
     Uint32 *dstp4;
     Uint8 *dstp1 = (Uint8 *) dst;
-    Uint32 value4 = (c | (c << 8) | (c << 16) | (c << 24));
-    Uint8 value1 = (Uint8) c;
+    Uint8 value1;
+    Uint32 value4;
+
+    /* The value used in memset() is a byte, passed as an int */
+    c &= 0xff;
 
     /* The destination pointer needs to be aligned on a 4-byte boundary to
      * execute a 32-bit set. Set first bytes manually if needed until it is
      * aligned. */
+    value1 = (Uint8)c;
     while ((intptr_t)dstp1 & 0x3) {
         if (len--) {
             *dstp1++ = value1;
@@ -285,6 +289,7 @@
         }
     }
 
+    value4 = (c | (c << 8) | (c << 16) | (c << 24));
     dstp4 = (Uint32 *) dstp1;
     left = (len % 4);
     len /= 4;
@@ -414,6 +419,17 @@
     }
     return len;
 #endif /* HAVE_STRLEN */
+}
+
+wchar_t *
+SDL_wcsdup(const wchar_t *string)
+{
+    size_t len = ((SDL_wcslen(string) + 1) * sizeof(wchar_t));
+    wchar_t *newstr = (wchar_t *)SDL_malloc(len);
+    if (newstr) {
+        SDL_memcpy(newstr, string, len);
+    }
+    return newstr;
 }
 
 size_t
@@ -562,7 +578,7 @@
 SDL_strdup(const char *string)
 {
     size_t len = SDL_strlen(string) + 1;
-    char *newstr = SDL_malloc(len);
+    char *newstr = (char *)SDL_malloc(len);
     if (newstr) {
         SDL_memcpy(newstr, string, len);
     }
@@ -1319,7 +1335,18 @@
     return retval;
 }
 
-#ifdef HAVE_VSNPRINTF
+#if defined(HAVE_LIBC) && defined(__WATCOMC__)
+/* _vsnprintf() doesn't ensure nul termination */
+int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
+{
+    int retval;
+    if (!fmt) fmt = "";
+    retval = _vsnprintf(text, maxlen, fmt, ap);
+    if (maxlen > 0) text[maxlen-1] = '\0';
+    if (retval < 0) retval = (int) maxlen;
+    return retval;
+}
+#elif defined(HAVE_VSNPRINTF)
 int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
 {
     if (!fmt) {
@@ -1338,9 +1365,9 @@
 
 typedef struct
 {
-    SDL_bool left_justify;
+    SDL_bool left_justify; /* for now: ignored. */
     SDL_bool force_sign;
-    SDL_bool force_type;
+    SDL_bool force_type;   /* for now: used only by float printer, ignored otherwise. */
     SDL_bool pad_zeroes;
     SDL_letter_case force_case;
     int width;
@@ -1352,15 +1379,18 @@
 SDL_PrintString(char *text, size_t maxlen, SDL_FormatInfo *info, const char *string)
 {
     size_t length = 0;
-    size_t slen;
+    size_t slen, sz;
 
     if (string == NULL) {
         string = "(null)";
     }
 
-    if (info && info->width && (size_t)info->width > SDL_strlen(string)) {
+    sz = SDL_strlen(string);
+    if (info && info->width > 0 && (size_t)info->width > sz) {
         char fill = info->pad_zeroes ? '0' : ' ';
-        size_t width = info->width - SDL_strlen(string);
+        size_t width = info->width - sz;
+        if (info->precision >= 0 && (size_t)info->precision < sz)
+            width += sz - (size_t)info->precision;
         while (width-- > 0 && maxlen > 0) {
             *text++ = fill;
             ++length;
@@ -1372,6 +1402,13 @@
     length += SDL_min(slen, maxlen);
 
     if (info) {
+        if (info->precision >= 0 && (size_t)info->precision < sz) {
+            slen = (size_t)info->precision;
+            if (slen < maxlen) {
+                text[slen] = 0;
+                length -= (sz - slen);
+            }
+        }
         if (info->force_case == SDL_CASE_LOWER) {
             SDL_strlwr(text);
         } else if (info->force_case == SDL_CASE_UPPER) {
@@ -1381,12 +1418,54 @@
     return length;
 }
 
+static void
+SDL_IntPrecisionAdjust(char *num, size_t maxlen, SDL_FormatInfo *info)
+{/* left-pad num with zeroes. */
+    size_t sz, pad, have_sign;
+
+    if (!info)
+        return;
+
+    have_sign = 0;
+    if (*num == '-' || *num == '+') {
+        have_sign = 1;
+        ++num;
+        --maxlen;
+    }
+    sz = SDL_strlen(num);
+    if (info->precision > 0 && sz < (size_t)info->precision) {
+        pad = (size_t)info->precision - sz;
+        if (pad + sz + 1 <= maxlen) { /* otherwise ignore the precision */
+            SDL_memmove(num + pad, num, sz + 1);
+            SDL_memset(num, '0', pad);
+        }
+    }
+    info->precision = -1;/* so that SDL_PrintString() doesn't make a mess. */
+
+    if (info->pad_zeroes && info->width > 0 && (size_t)info->width > sz + have_sign) {
+    /* handle here: spaces are added before the sign
+       but zeroes must be placed _after_ the sign. */
+    /* sz hasn't changed: we ignore pad_zeroes if a precision is given. */
+        pad = (size_t)info->width - sz - have_sign;
+        if (pad + sz + 1 <= maxlen) {
+            SDL_memmove(num + pad, num, sz + 1);
+            SDL_memset(num, '0', pad);
+        }
+        info->width = 0; /* so that SDL_PrintString() doesn't make a mess. */
+    }
+}
+
 static size_t
 SDL_PrintLong(char *text, size_t maxlen, SDL_FormatInfo *info, long value)
 {
-    char num[130];
+    char num[130], *p = num;
 
-    SDL_ltoa(value, num, info ? info->radix : 10);
+    if (info->force_sign && value >= 0L) {
+        *p++ = '+';
+    }
+
+    SDL_ltoa(value, p, info ? info->radix : 10);
+    SDL_IntPrecisionAdjust(num, maxlen, info);
     return SDL_PrintString(text, maxlen, info, num);
 }
 
@@ -1396,15 +1475,21 @@
     char num[130];
 
     SDL_ultoa(value, num, info ? info->radix : 10);
+    SDL_IntPrecisionAdjust(num, maxlen, info);
     return SDL_PrintString(text, maxlen, info, num);
 }
 
 static size_t
 SDL_PrintLongLong(char *text, size_t maxlen, SDL_FormatInfo *info, Sint64 value)
 {
-    char num[130];
+    char num[130], *p = num;
 
-    SDL_lltoa(value, num, info ? info->radix : 10);
+    if (info->force_sign && value >= (Sint64)0) {
+        *p++ = '+';
+    }
+
+    SDL_lltoa(value, p, info ? info->radix : 10);
+    SDL_IntPrecisionAdjust(num, maxlen, info);
     return SDL_PrintString(text, maxlen, info, num);
 }
 
@@ -1414,6 +1499,7 @@
     char num[130];
 
     SDL_ulltoa(value, num, info ? info->radix : 10);
+    SDL_IntPrecisionAdjust(num, maxlen, info);
     return SDL_PrintString(text, maxlen, info, num);
 }
 
@@ -1571,12 +1657,22 @@
             if (*fmt >= '0' && *fmt <= '9') {
                 info.width = SDL_strtol(fmt, (char **)&fmt, 0);
             }
+            else if (*fmt == '*') {
+                ++fmt;
+                info.width = va_arg(ap, int);
+            }
 
             if (*fmt == '.') {
                 ++fmt;
                 if (*fmt >= '0' && *fmt <= '9') {
                     info.precision = SDL_strtol(fmt, (char **)&fmt, 0);
+                } else if (*fmt == '*') {
+                    ++fmt;
+                    info.precision = va_arg(ap, int);
                 } else {
+                    info.precision = 0;
+                }
+                if (info.precision < 0) {
                     info.precision = 0;
                 }
             }
@@ -1614,6 +1710,9 @@
                     break;
                 case 'i':
                 case 'd':
+                    if (info.precision >= 0) {
+                        info.pad_zeroes = SDL_FALSE;
+                    }
                     switch (inttype) {
                     case DO_INT:
                         len = SDL_PrintLong(text, left, &info,
@@ -1651,7 +1750,10 @@
                     }
                     /* Fall through to unsigned handling */
                 case 'u':
-                    info.pad_zeroes = SDL_TRUE;
+                    info.force_sign = SDL_FALSE;
+                    if (info.precision >= 0) {
+                        info.pad_zeroes = SDL_FALSE;
+                    }
                     switch (inttype) {
                     case DO_INT:
                         len = SDL_PrintUnsignedLong(text, left, &info,
@@ -1678,12 +1780,14 @@
                         /* In practice this is used on Windows for WCHAR strings */
                         wchar_t *wide_arg = va_arg(ap, wchar_t *);
                         char *arg = SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(wide_arg), (SDL_wcslen(wide_arg)+1)*sizeof(*wide_arg));
+                        info.pad_zeroes = SDL_FALSE;
                         len = SDL_PrintString(text, left, &info, arg);
                         SDL_free(arg);
                         done = SDL_TRUE;
                     }
                     break;
                 case 's':
+                    info.pad_zeroes = SDL_FALSE;
                     len = SDL_PrintString(text, left, &info, va_arg(ap, char *));
                     done = SDL_TRUE;
                     break;
diff --git a/source/src/test/SDL_test_common.c b/source/src/test/SDL_test_common.c
index b7e294a..81dd39e 100644
--- a/source/src/test/SDL_test_common.c
+++ b/source/src/test/SDL_test_common.c
@@ -1049,6 +1049,22 @@
 }
 
 static const char *
+DisplayOrientationName(int orientation)
+{
+    switch (orientation)
+    {
+#define CASE(X) case SDL_ORIENTATION_##X: return #X
+        CASE(UNKNOWN);
+        CASE(LANDSCAPE);
+        CASE(LANDSCAPE_FLIPPED);
+        CASE(PORTRAIT);
+        CASE(PORTRAIT_FLIPPED);
+#undef CASE
+default: return "???";
+    }
+}
+
+static const char *
 ControllerAxisName(const SDL_GameControllerAxis axis)
 {
     switch (axis)
@@ -1102,6 +1118,17 @@
     }
 
     switch (event->type) {
+    case SDL_DISPLAYEVENT:
+        switch (event->display.event) {
+        case SDL_DISPLAYEVENT_ORIENTATION:
+            SDL_Log("SDL EVENT: Display %d changed orientation to %s", event->display.display, DisplayOrientationName(event->display.data1));
+            break;
+        default:
+            SDL_Log("SDL EVENT: Display %d got unknown event 0x%4.4x",
+                    event->display.display, event->display.event);
+            break;
+        }
+        break;
     case SDL_WINDOWEVENT:
         switch (event->window.event) {
         case SDL_WINDOWEVENT_SHOWN:
@@ -1349,7 +1376,18 @@
     case SDL_APP_DIDENTERFOREGROUND:
         SDL_Log("SDL EVENT: App entered the foreground");
         break;
-
+    case SDL_DROPBEGIN:
+        SDL_Log("SDL EVENT: Drag and drop beginning");
+        break;
+    case SDL_DROPFILE:
+        SDL_Log("SDL EVENT: Drag and drop file: '%s'", event->drop.file);
+        break;
+    case SDL_DROPTEXT:
+        SDL_Log("SDL EVENT: Drag and drop text: '%s'", event->drop.file);
+        break;
+    case SDL_DROPCOMPLETE:
+        SDL_Log("SDL EVENT: Drag and drop ending");
+        break;
     case SDL_QUIT:
         SDL_Log("SDL EVENT: Quit requested");
         break;
@@ -1744,6 +1782,11 @@
     case SDL_MOUSEMOTION:
         lastEvent = event->motion;
         break;
+
+    case SDL_DROPFILE:
+    case SDL_DROPTEXT:
+        SDL_free(event->drop.file);
+        break;
     }
 }
 
diff --git a/source/src/test/SDL_test_harness.c b/source/src/test/SDL_test_harness.c
index 15021c6..80b0794 100644
--- a/source/src/test/SDL_test_harness.c
+++ b/source/src/test/SDL_test_harness.c
@@ -206,6 +206,9 @@
 /**
 * \brief Timeout handler. Aborts test run and exits harness process.
 */
+#if defined(__WATCOMC__)
+#pragma aux SDLTest_BailOut aborts;
+#endif
 static SDL_NORETURN void
 SDLTest_BailOut()
 {
diff --git a/source/src/thread/SDL_thread.c b/source/src/thread/SDL_thread.c
index 84b72d5..5570adb 100644
--- a/source/src/thread/SDL_thread.c
+++ b/source/src/thread/SDL_thread.c
@@ -299,19 +299,21 @@
 
 #ifdef SDL_CreateThread
 #undef SDL_CreateThread
+#undef SDL_CreateThreadWithStackSize
 #endif
 #if SDL_DYNAMIC_API
 #define SDL_CreateThread SDL_CreateThread_REAL
+#define SDL_CreateThreadWithStackSize SDL_CreateThreadWithStackSize_REAL
 #endif
 
 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
-static SDL_Thread *
+SDL_Thread *
 SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
                  const char *name, const size_t stacksize, void *data,
                  pfnSDL_CurrentBeginThread pfnBeginThread,
                  pfnSDL_CurrentEndThread pfnEndThread)
 #else
-static SDL_Thread *
+SDL_Thread *
 SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
                 const char *name, const size_t stacksize, void *data)
 #endif
diff --git a/source/src/thread/psp/SDL_systhread.c b/source/src/thread/psp/SDL_systhread.c
index 9286be5..284f182 100644
--- a/source/src/thread/psp/SDL_systhread.c
+++ b/source/src/thread/psp/SDL_systhread.c
@@ -97,6 +97,8 @@
     if (priority == SDL_THREAD_PRIORITY_LOW) {
         value = 19;
     } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
+        value = -10;
+    } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
         value = -20;
     } else {
         value = 0;
diff --git a/source/src/thread/pthread/SDL_sysmutex.c b/source/src/thread/pthread/SDL_sysmutex.c
index e7b5b5c..e514778 100644
--- a/source/src/thread/pthread/SDL_sysmutex.c
+++ b/source/src/thread/pthread/SDL_sysmutex.c
@@ -116,6 +116,7 @@
 SDL_TryLockMutex(SDL_mutex * mutex)
 {
     int retval;
+    int result;
 #if FAKE_RECURSIVE_MUTEX
     pthread_t this_thread;
 #endif
@@ -134,18 +135,20 @@
          We set the locking thread id after we obtain the lock
          so unlocks from other threads will fail.
          */
-        if (pthread_mutex_trylock(&mutex->id) == 0) {
+        result = pthread_mutex_trylock(&mutex->id);
+        if (result == 0) {
             mutex->owner = this_thread;
             mutex->recursive = 0;
-        } else if (errno == EBUSY) {
+        } else if (result == EBUSY) {
             retval = SDL_MUTEX_TIMEDOUT;
         } else {
             retval = SDL_SetError("pthread_mutex_trylock() failed");
         }
     }
 #else
-    if (pthread_mutex_trylock(&mutex->id) != 0) {
-        if (errno == EBUSY) {
+    result = pthread_mutex_trylock(&mutex->id);
+    if (result != 0) {
+        if (result == EBUSY) {
             retval = SDL_MUTEX_TIMEDOUT;
         } else {
             retval = SDL_SetError("pthread_mutex_trylock() failed");
diff --git a/source/src/thread/pthread/SDL_systhread.c b/source/src/thread/pthread/SDL_systhread.c
index 0354840..ec32937 100644
--- a/source/src/thread/pthread/SDL_systhread.c
+++ b/source/src/thread/pthread/SDL_systhread.c
@@ -34,6 +34,9 @@
 #include <sys/resource.h>
 #include <sys/syscall.h>
 #include <unistd.h>
+#include <errno.h>
+
+#include "../../core/linux/SDL_dbus.h"
 #endif /* __LINUX__ */
 
 #if defined(__LINUX__) || defined(__MACOSX__) || defined(__IPHONEOS__)
@@ -43,6 +46,7 @@
 #endif
 #endif
 
+#include "SDL_log.h"
 #include "SDL_platform.h"
 #include "SDL_thread.h"
 #include "../SDL_thread_c.h"
@@ -180,6 +184,84 @@
     return ((SDL_threadID) pthread_self());
 }
 
+#if __LINUX__
+/* d-bus queries to org.freedesktop.RealtimeKit1. */
+#if SDL_USE_LIBDBUS
+
+#define RTKIT_DBUS_NODE "org.freedesktop.RealtimeKit1"
+#define RTKIT_DBUS_PATH "/org/freedesktop/RealtimeKit1"
+#define RTKIT_DBUS_INTERFACE "org.freedesktop.RealtimeKit1"
+
+static pthread_once_t rtkit_initialize_once = PTHREAD_ONCE_INIT;
+static Sint32 rtkit_min_nice_level = -20;
+
+static void
+rtkit_initialize()
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    /* Try getting minimum nice level: this is often greater than PRIO_MIN (-20). */
+    if (!dbus || !SDL_DBus_QueryPropertyOnConnection(dbus->system_conn, RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MinNiceLevel",
+                                            DBUS_TYPE_INT32, &rtkit_min_nice_level)) {
+        rtkit_min_nice_level = -20;
+    }
+}
+
+static SDL_bool
+rtkit_setpriority(pid_t thread, int nice_level)
+{
+    Uint64 ui64 = (Uint64)thread;
+    Sint32 si32 = (Sint32)nice_level;
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    pthread_once(&rtkit_initialize_once, rtkit_initialize);
+
+    if (si32 < rtkit_min_nice_level)
+        si32 = rtkit_min_nice_level;
+
+    if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn,
+            RTKIT_DBUS_NODE, RTKIT_DBUS_PATH, RTKIT_DBUS_INTERFACE, "MakeThreadHighPriority",
+            DBUS_TYPE_UINT64, &ui64, DBUS_TYPE_INT32, &si32, DBUS_TYPE_INVALID,
+            DBUS_TYPE_INVALID)) {
+        return SDL_FALSE;
+    }
+    return SDL_TRUE;
+}
+
+#else
+
+static SDL_bool
+rtkit_setpriority(pid_t thread, int nice_level)
+{
+    return SDL_FALSE;
+}
+
+#endif /* !SDL_USE_LIBDBUS */
+
+int
+SDL_LinuxSetThreadPriority(Sint64 threadID, int priority)
+{
+    if (setpriority(PRIO_PROCESS, (id_t)threadID, priority) < 0) {
+        /* Note that this fails if you're trying to set high priority
+           and you don't have root permission. BUT DON'T RUN AS ROOT!
+
+           You can grant the ability to increase thread priority by
+           running the following command on your application binary:
+               sudo setcap 'cap_sys_nice=eip' <application>
+
+           Let's try setting priority with RealtimeKit...
+
+           README and sample code at:
+             http://git.0pointer.net/rtkit.git
+         */
+        if (rtkit_setpriority((pid_t)threadID, priority) == SDL_FALSE) {
+            return SDL_SetError("setpriority() failed");
+        }
+    }
+    return 0;
+}
+#endif /* __LINUX__ */
+
 int
 SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
 {
@@ -188,25 +270,18 @@
     return 0;
 #elif __LINUX__
     int value;
+    pid_t thread = syscall(SYS_gettid);
 
     if (priority == SDL_THREAD_PRIORITY_LOW) {
         value = 19;
     } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
+        value = -10;
+    } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
         value = -20;
     } else {
         value = 0;
     }
-    if (setpriority(PRIO_PROCESS, syscall(SYS_gettid), value) < 0) {
-        /* Note that this fails if you're trying to set high priority
-           and you don't have root permission. BUT DON'T RUN AS ROOT!
-
-           You can grant the ability to increase thread priority by
-           running the following command on your application binary:
-               sudo setcap 'cap_sys_nice=eip' <application>
-         */
-        return SDL_SetError("setpriority() failed");
-    }
-    return 0;
+    return SDL_LinuxSetThreadPriority(thread, value);
 #else
     struct sched_param sched;
     int policy;
@@ -217,12 +292,15 @@
     }
     if (priority == SDL_THREAD_PRIORITY_LOW) {
         sched.sched_priority = sched_get_priority_min(policy);
-    } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
+    } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
         sched.sched_priority = sched_get_priority_max(policy);
     } else {
         int min_priority = sched_get_priority_min(policy);
         int max_priority = sched_get_priority_max(policy);
         sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
+        if (priority == SDL_THREAD_PRIORITY_HIGH) {
+            sched.sched_priority += ((max_priority - min_priority) / 4);
+        }
     }
     if (pthread_setschedparam(thread, policy, &sched) != 0) {
         return SDL_SetError("pthread_setschedparam() failed");
diff --git a/source/src/thread/windows/SDL_systhread.c b/source/src/thread/windows/SDL_systhread.c
index 90036c9..251510d 100644
--- a/source/src/thread/windows/SDL_systhread.c
+++ b/source/src/thread/windows/SDL_systhread.c
@@ -231,6 +231,8 @@
         value = THREAD_PRIORITY_LOWEST;
     } else if (priority == SDL_THREAD_PRIORITY_HIGH) {
         value = THREAD_PRIORITY_HIGHEST;
+    } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
+        value = THREAD_PRIORITY_TIME_CRITICAL;
     } else {
         value = THREAD_PRIORITY_NORMAL;
     }
diff --git a/source/src/timer/SDL_timer_c.h b/source/src/timer/SDL_timer_c.h
index f83bdde..3ea350f 100644
--- a/source/src/timer/SDL_timer_c.h
+++ b/source/src/timer/SDL_timer_c.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_timer_c_h_
+#define SDL_timer_c_h_
+
 #include "../SDL_internal.h"
 
 /* Useful functions and variables from SDL_timer.c */
@@ -31,4 +35,6 @@
 extern int SDL_TimerInit(void);
 extern void SDL_TimerQuit(void);
 
+#endif /* SDL_timer_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_RLEaccel_c.h b/source/src/video/SDL_RLEaccel_c.h
index fe41835..b6fa6a1 100644
--- a/source/src/video/SDL_RLEaccel_c.h
+++ b/source/src/video/SDL_RLEaccel_c.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_RLEaccel_c_h_
+#define SDL_RLEaccel_c_h_
+
 #include "../SDL_internal.h"
 
 /* Useful functions and variables from SDL_RLEaccel.c */
@@ -28,4 +32,7 @@
 extern int SDLCALL SDL_RLEAlphaBlit(SDL_Surface * src, SDL_Rect * srcrect,
                                     SDL_Surface * dst, SDL_Rect * dstrect);
 extern void SDL_UnRLESurface(SDL_Surface * surface, int recode);
+
+#endif /* SDL_RLEaccel_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_blit.h b/source/src/video/SDL_blit.h
index ca10534..6c95aaf 100644
--- a/source/src/video/SDL_blit.h
+++ b/source/src/video/SDL_blit.h
@@ -126,7 +126,7 @@
     b = SDL_expand_byte[fmt->Bloss][((Pixel&fmt->Bmask)>>fmt->Bshift)]; \
 }
 #define RGB_FROM_RGB565(Pixel, r, g, b)                                 \
-    {                                                                   \
+{                                                                       \
     r = SDL_expand_byte[3][((Pixel&0xF800)>>11)];                       \
     g = SDL_expand_byte[2][((Pixel&0x07E0)>>5)];                        \
     b = SDL_expand_byte[3][(Pixel&0x001F)];                             \
@@ -262,18 +262,18 @@
 {                                                                       \
     switch (bpp) {                                                      \
         case 1: {                                                       \
-            Uint8 Pixel;                                                \
+            Uint8 _Pixel;                                               \
                                                                         \
-            PIXEL_FROM_RGB(Pixel, fmt, r, g, b);                        \
-            *((Uint8 *)(buf)) = Pixel;                                  \
+            PIXEL_FROM_RGB(_Pixel, fmt, r, g, b);                       \
+            *((Uint8 *)(buf)) = _Pixel;                                 \
         }                                                               \
         break;                                                          \
                                                                         \
         case 2: {                                                       \
-            Uint16 Pixel;                                               \
+            Uint16 _Pixel;                                              \
                                                                         \
-            PIXEL_FROM_RGB(Pixel, fmt, r, g, b);                        \
-            *((Uint16 *)(buf)) = Pixel;                                 \
+            PIXEL_FROM_RGB(_Pixel, fmt, r, g, b);                       \
+            *((Uint16 *)(buf)) = _Pixel;                                \
         }                                                               \
         break;                                                          \
                                                                         \
@@ -291,10 +291,10 @@
         break;                                                          \
                                                                         \
         case 4: {                                                       \
-            Uint32 Pixel;                                               \
+            Uint32 _Pixel;                                              \
                                                                         \
-            PIXEL_FROM_RGB(Pixel, fmt, r, g, b);                        \
-            *((Uint32 *)(buf)) = Pixel;                                 \
+            PIXEL_FROM_RGB(_Pixel, fmt, r, g, b);                       \
+            *((Uint32 *)(buf)) = _Pixel;                                \
         }                                                               \
         break;                                                          \
     }                                                                   \
diff --git a/source/src/video/SDL_blit_1.c b/source/src/video/SDL_blit_1.c
index b7c5412..56ccf15 100644
--- a/source/src/video/SDL_blit_1.c
+++ b/source/src/video/SDL_blit_1.c
@@ -49,13 +49,13 @@
     while (height--) {
 #ifdef USE_DUFFS_LOOP
         /* *INDENT-OFF* */
-		DUFFS_LOOP(
-			{
-			  *dst = map[*src];
-			}
-			dst++;
-			src++;
-		, width);
+        DUFFS_LOOP(
+            {
+              *dst = map[*src];
+            }
+            dst++;
+            src++;
+        , width);
         /* *INDENT-ON* */
 #else
         for (c = width; c; --c) {
@@ -72,11 +72,11 @@
 /* This is now endian dependent */
 #ifndef USE_DUFFS_LOOP
 # if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
-#  define HI	1
-#  define LO	0
+#  define HI    1
+#  define LO    0
 # else /* ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) */
-#  define HI	0
-#  define LO	1
+#  define HI    0
+#  define LO    1
 # endif
 #endif
 static void
@@ -101,14 +101,14 @@
 
 #ifdef USE_DUFFS_LOOP
     while (height--) {
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-		{
-			*(Uint16 *)dst = map[*src++];
-			dst += 2;
-		},
-		width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+        {
+            *(Uint16 *)dst = map[*src++];
+            dst += 2;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
@@ -208,18 +208,18 @@
 
     while (height--) {
 #ifdef USE_DUFFS_LOOP
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-			{
-				o = *src * 4;
-				dst[0] = map[o++];
-				dst[1] = map[o++];
-				dst[2] = map[o++];
-			}
-			src++;
-			dst += 3;
-		, width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+            {
+                o = *src * 4;
+                dst[0] = map[o++];
+                dst[1] = map[o++];
+                dst[2] = map[o++];
+            }
+            src++;
+            dst += 3;
+        , width);
+        /* *INDENT-ON* */
 #else
         for (c = width; c; --c) {
             o = *src * 4;
@@ -257,11 +257,11 @@
 
     while (height--) {
 #ifdef USE_DUFFS_LOOP
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-			*dst++ = map[*src++];
-		, width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+            *dst++ = map[*src++];
+        , width);
+        /* *INDENT-ON* */
 #else
         for (c = width / 4; c; --c) {
             *dst++ = map[*src++];
@@ -297,33 +297,33 @@
 
     if (palmap) {
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP(
-			{
-				if ( *src != ckey ) {
-				  *dst = palmap[*src];
-				}
-				dst++;
-				src++;
-			},
-			width);
-			/* *INDENT-ON* */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP(
+            {
+                if ( *src != ckey ) {
+                  *dst = palmap[*src];
+                }
+                dst++;
+                src++;
+            },
+            width);
+            /* *INDENT-ON* */
             src += srcskip;
             dst += dstskip;
         }
     } else {
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP(
-			{
-				if ( *src != ckey ) {
-				  *dst = *src;
-				}
-				dst++;
-				src++;
-			},
-			width);
-			/* *INDENT-ON* */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP(
+            {
+                if ( *src != ckey ) {
+                  *dst = *src;
+                }
+                dst++;
+                src++;
+            },
+            width);
+            /* *INDENT-ON* */
             src += srcskip;
             dst += dstskip;
         }
@@ -346,17 +346,17 @@
     dstskip /= 2;
 
     while (height--) {
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-		{
-			if ( *src != ckey ) {
-				*dstp=palmap[*src];
-			}
-			src++;
-			dstp++;
-		},
-		width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+        {
+            if ( *src != ckey ) {
+                *dstp=palmap[*src];
+            }
+            src++;
+            dstp++;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dstp += dstskip;
     }
@@ -376,20 +376,20 @@
     int o;
 
     while (height--) {
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-		{
-			if ( *src != ckey ) {
-				o = *src * 4;
-				dst[0] = palmap[o++];
-				dst[1] = palmap[o++];
-				dst[2] = palmap[o++];
-			}
-			src++;
-			dst += 3;
-		},
-		width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+        {
+            if ( *src != ckey ) {
+                o = *src * 4;
+                dst[0] = palmap[o++];
+                dst[1] = palmap[o++];
+                dst[2] = palmap[o++];
+            }
+            src++;
+            dst += 3;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
@@ -411,17 +411,17 @@
     dstskip /= 4;
 
     while (height--) {
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-		{
-			if ( *src != ckey ) {
-				*dstp = palmap[*src];
-			}
-			src++;
-			dstp++;
-		},
-		width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+        {
+            if ( *src != ckey ) {
+                *dstp = palmap[*src];
+            }
+            src++;
+            dstp++;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dstp += dstskip;
     }
@@ -489,22 +489,22 @@
     dstbpp = dstfmt->BytesPerPixel;
 
     while (height--) {
-		/* *INDENT-OFF* */
-		DUFFS_LOOP(
-		{
-			if ( *src != ckey ) {
-				sR = srcpal[*src].r;
-				sG = srcpal[*src].g;
-				sB = srcpal[*src].b;
-				DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
-				ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
-			  	ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
-			}
-			src++;
-			dst += dstbpp;
-		},
-		width);
-		/* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+        {
+            if ( *src != ckey ) {
+                sR = srcpal[*src].r;
+                sG = srcpal[*src].g;
+                sB = srcpal[*src].b;
+                DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
+                ALPHA_BLEND_RGBA(sR, sG, sB, A, dR, dG, dB, dA);
+                  ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
+            }
+            src++;
+            dst += dstbpp;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
diff --git a/source/src/video/SDL_blit_A.c b/source/src/video/SDL_blit_A.c
index 1e9c9d8..3507932 100644
--- a/source/src/video/SDL_blit_A.c
+++ b/source/src/video/SDL_blit_A.c
@@ -45,28 +45,28 @@
     const unsigned A = info->a;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4(
-	    {
-		DISEMBLE_RGB(src, srcbpp, srcfmt, Pixel, sR, sG, sB);
-		dR = dstfmt->palette->colors[*dst].r;
-		dG = dstfmt->palette->colors[*dst].g;
-		dB = dstfmt->palette->colors[*dst].b;
-		ALPHA_BLEND_RGB(sR, sG, sB, A, dR, dG, dB);
-		dR &= 0xff;
-		dG &= 0xff;
-		dB &= 0xff;
-		/* Pack RGB into 8bit pixel */
-		if ( palmap == NULL ) {
-		    *dst =((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0));
-		} else {
-		    *dst = palmap[((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0))];
-		}
-		dst++;
-		src += srcbpp;
-	    },
-	    width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4(
+        {
+        DISEMBLE_RGB(src, srcbpp, srcfmt, Pixel, sR, sG, sB);
+        dR = dstfmt->palette->colors[*dst].r;
+        dG = dstfmt->palette->colors[*dst].g;
+        dB = dstfmt->palette->colors[*dst].b;
+        ALPHA_BLEND_RGB(sR, sG, sB, A, dR, dG, dB);
+        dR &= 0xff;
+        dG &= 0xff;
+        dB &= 0xff;
+        /* Pack RGB into 8bit pixel */
+        if ( palmap == NULL ) {
+            *dst =((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0));
+        } else {
+            *dst = palmap[((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0))];
+        }
+        dst++;
+        src += srcbpp;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
@@ -91,28 +91,28 @@
     unsigned dR, dG, dB;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4(
-	    {
-		DISEMBLE_RGBA(src,srcbpp,srcfmt,Pixel,sR,sG,sB,sA);
-		dR = dstfmt->palette->colors[*dst].r;
-		dG = dstfmt->palette->colors[*dst].g;
-		dB = dstfmt->palette->colors[*dst].b;
-		ALPHA_BLEND_RGB(sR, sG, sB, sA, dR, dG, dB);
-		dR &= 0xff;
-		dG &= 0xff;
-		dB &= 0xff;
-		/* Pack RGB into 8bit pixel */
-		if ( palmap == NULL ) {
-		    *dst =((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0));
-		} else {
-		    *dst = palmap[((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0))];
-		}
-		dst++;
-		src += srcbpp;
-	    },
-	    width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4(
+        {
+        DISEMBLE_RGBA(src,srcbpp,srcfmt,Pixel,sR,sG,sB,sA);
+        dR = dstfmt->palette->colors[*dst].r;
+        dG = dstfmt->palette->colors[*dst].g;
+        dB = dstfmt->palette->colors[*dst].b;
+        ALPHA_BLEND_RGB(sR, sG, sB, sA, dR, dG, dB);
+        dR &= 0xff;
+        dG &= 0xff;
+        dB &= 0xff;
+        /* Pack RGB into 8bit pixel */
+        if ( palmap == NULL ) {
+            *dst =((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0));
+        } else {
+            *dst = palmap[((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0))];
+        }
+        dst++;
+        src += srcbpp;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
@@ -139,30 +139,30 @@
     const unsigned A = info->a;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP(
-	    {
-		DISEMBLE_RGB(src, srcbpp, srcfmt, Pixel, sR, sG, sB);
-		if ( Pixel != ckey ) {
-		    dR = dstfmt->palette->colors[*dst].r;
-		    dG = dstfmt->palette->colors[*dst].g;
-		    dB = dstfmt->palette->colors[*dst].b;
-		    ALPHA_BLEND_RGB(sR, sG, sB, A, dR, dG, dB);
-		    dR &= 0xff;
-		    dG &= 0xff;
-		    dB &= 0xff;
-		    /* Pack RGB into 8bit pixel */
-		    if ( palmap == NULL ) {
+        /* *INDENT-OFF* */
+        DUFFS_LOOP(
+        {
+        DISEMBLE_RGB(src, srcbpp, srcfmt, Pixel, sR, sG, sB);
+        if ( Pixel != ckey ) {
+            dR = dstfmt->palette->colors[*dst].r;
+            dG = dstfmt->palette->colors[*dst].g;
+            dB = dstfmt->palette->colors[*dst].b;
+            ALPHA_BLEND_RGB(sR, sG, sB, A, dR, dG, dB);
+            dR &= 0xff;
+            dG &= 0xff;
+            dB &= 0xff;
+            /* Pack RGB into 8bit pixel */
+            if ( palmap == NULL ) {
                 *dst =((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0));
-		    } else {
+            } else {
                 *dst = palmap[((dR>>5)<<(3+2))|((dG>>5)<<(2))|((dB>>6)<<(0))];
-		    }
-		}
-		dst++;
-		src += srcbpp;
-	    },
-	    width);
-	    /* *INDENT-ON* */
+            }
+        }
+        dst++;
+        src += srcbpp;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
@@ -342,45 +342,45 @@
 
     mm_zero = _mm_setzero_si64();       /* 0 -> mm_zero */
     multmask = 0x00FF;
-	multmask <<= (ashift * 2);
-	multmask2 = 0x00FF00FF00FF00FFULL;
+    multmask <<= (ashift * 2);
+    multmask2 = 0x00FF00FF00FF00FFULL;
 
     while (height--) {
-		/* *INDENT-OFF* */
-		DUFFS_LOOP4({
-		Uint32 alpha = *srcp & amask;
-		if (alpha == 0) {
-			/* do nothing */
-		} else if (alpha == amask) {
-			*dstp = *srcp;
-		} else {
-			src1 = _mm_cvtsi32_si64(*srcp); /* src(ARGB) -> src1 (0000ARGB) */
-			src1 = _mm_unpacklo_pi8(src1, mm_zero); /* 0A0R0G0B -> src1 */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4({
+        Uint32 alpha = *srcp & amask;
+        if (alpha == 0) {
+            /* do nothing */
+        } else if (alpha == amask) {
+            *dstp = *srcp;
+        } else {
+            src1 = _mm_cvtsi32_si64(*srcp); /* src(ARGB) -> src1 (0000ARGB) */
+            src1 = _mm_unpacklo_pi8(src1, mm_zero); /* 0A0R0G0B -> src1 */
 
-			dst1 = _mm_cvtsi32_si64(*dstp); /* dst(ARGB) -> dst1 (0000ARGB) */
-			dst1 = _mm_unpacklo_pi8(dst1, mm_zero); /* 0A0R0G0B -> dst1 */
+            dst1 = _mm_cvtsi32_si64(*dstp); /* dst(ARGB) -> dst1 (0000ARGB) */
+            dst1 = _mm_unpacklo_pi8(dst1, mm_zero); /* 0A0R0G0B -> dst1 */
 
-			mm_alpha = _mm_cvtsi32_si64(alpha); /* alpha -> mm_alpha (0000000A) */
-			mm_alpha = _mm_srli_si64(mm_alpha, ashift); /* mm_alpha >> ashift -> mm_alpha(0000000A) */
-			mm_alpha = _mm_unpacklo_pi16(mm_alpha, mm_alpha); /* 00000A0A -> mm_alpha */
-			mm_alpha2 = _mm_unpacklo_pi32(mm_alpha, mm_alpha); /* 0A0A0A0A -> mm_alpha2 */
-			mm_alpha = _mm_or_si64(mm_alpha2, *(__m64 *) & multmask);	/* 0F0A0A0A -> mm_alpha */
-			mm_alpha2 = _mm_xor_si64(mm_alpha2, *(__m64 *) & multmask2);	/* 255 - mm_alpha -> mm_alpha */
+            mm_alpha = _mm_cvtsi32_si64(alpha); /* alpha -> mm_alpha (0000000A) */
+            mm_alpha = _mm_srli_si64(mm_alpha, ashift); /* mm_alpha >> ashift -> mm_alpha(0000000A) */
+            mm_alpha = _mm_unpacklo_pi16(mm_alpha, mm_alpha); /* 00000A0A -> mm_alpha */
+            mm_alpha2 = _mm_unpacklo_pi32(mm_alpha, mm_alpha); /* 0A0A0A0A -> mm_alpha2 */
+            mm_alpha = _mm_or_si64(mm_alpha2, *(__m64 *) & multmask);    /* 0F0A0A0A -> mm_alpha */
+            mm_alpha2 = _mm_xor_si64(mm_alpha2, *(__m64 *) & multmask2);    /* 255 - mm_alpha -> mm_alpha */
 
-			/* blend */		    
-			src1 = _mm_mullo_pi16(src1, mm_alpha);
-			src1 = _mm_srli_pi16(src1, 8);
-			dst1 = _mm_mullo_pi16(dst1, mm_alpha2);
-			dst1 = _mm_srli_pi16(dst1, 8);
-			dst1 = _mm_add_pi16(src1, dst1);
-			dst1 = _mm_packs_pu16(dst1, mm_zero);
-			
-			*dstp = _mm_cvtsi64_si32(dst1); /* dst1 -> pixel */
-		}
-		++srcp;
-		++dstp;
-	    }, width);
-		/* *INDENT-ON* */
+            /* blend */            
+            src1 = _mm_mullo_pi16(src1, mm_alpha);
+            src1 = _mm_srli_pi16(src1, 8);
+            dst1 = _mm_mullo_pi16(dst1, mm_alpha2);
+            dst1 = _mm_srli_pi16(dst1, 8);
+            dst1 = _mm_add_pi16(src1, dst1);
+            dst1 = _mm_packs_pu16(dst1, mm_zero);
+            
+            *dstp = _mm_cvtsi64_si32(dst1); /* dst1 -> pixel */
+        }
+        ++srcp;
+        ++dstp;
+        }, width);
+        /* *INDENT-ON* */
         srcp += srcskip;
         dstp += dstskip;
     }
@@ -401,14 +401,14 @@
     int dstskip = info->dst_skip >> 2;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4({
-		    Uint32 s = *srcp++;
-		    Uint32 d = *dstp;
-		    *dstp++ = ((((s & 0x00fefefe) + (d & 0x00fefefe)) >> 1)
-			       + (s & d & 0x00010101)) | 0xff000000;
-	    }, width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4({
+            Uint32 s = *srcp++;
+            Uint32 d = *dstp;
+            *dstp++ = ((((s & 0x00fefefe) + (d & 0x00fefefe)) >> 1)
+                   + (s & d & 0x00010101)) | 0xff000000;
+        }, width);
+        /* *INDENT-ON* */
         srcp += srcskip;
         dstp += dstskip;
     }
@@ -434,22 +434,22 @@
         Uint32 d1;
 
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP4({
-				s = *srcp;
-				d = *dstp;
-				s1 = s & 0xff00ff;
-				d1 = d & 0xff00ff;
-				d1 = (d1 + ((s1 - d1) * alpha >> 8))
-				     & 0xff00ff;
-				s &= 0xff00;
-				d &= 0xff00;
-				d = (d + ((s - d) * alpha >> 8)) & 0xff00;
-				*dstp = d1 | d | 0xff000000;
-				++srcp;
-				++dstp;
-			}, width);
-			/* *INDENT-ON* */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP4({
+                s = *srcp;
+                d = *dstp;
+                s1 = s & 0xff00ff;
+                d1 = d & 0xff00ff;
+                d1 = (d1 + ((s1 - d1) * alpha >> 8))
+                     & 0xff00ff;
+                s &= 0xff00;
+                d &= 0xff00;
+                d = (d + ((s - d) * alpha >> 8)) & 0xff00;
+                *dstp = d1 | d | 0xff000000;
+                ++srcp;
+                ++dstp;
+            }, width);
+            /* *INDENT-ON* */
             srcp += srcskip;
             dstp += dstskip;
         }
@@ -468,42 +468,42 @@
     int dstskip = info->dst_skip >> 2;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4({
-		Uint32 dalpha;
-		Uint32 d;
-		Uint32 s1;
-		Uint32 d1;
-		Uint32 s = *srcp;
-		Uint32 alpha = s >> 24;
-		/* FIXME: Here we special-case opaque alpha since the
-		   compositioning used (>>8 instead of /255) doesn't handle
-		   it correctly. Also special-case alpha=0 for speed?
-		   Benchmark this! */
-		if (alpha) {
-		  if (alpha == SDL_ALPHA_OPAQUE) {
-			  *dstp = *srcp;
-		  } else {
-		    /*
-		     * take out the middle component (green), and process
-		     * the other two in parallel. One multiply less.
-		     */
-		    d = *dstp;
-			dalpha = d >> 24;
-		    s1 = s & 0xff00ff;
-		    d1 = d & 0xff00ff;
-		    d1 = (d1 + ((s1 - d1) * alpha >> 8)) & 0xff00ff;
-		    s &= 0xff00;
-		    d &= 0xff00;
-		    d = (d + ((s - d) * alpha >> 8)) & 0xff00;
-			dalpha = alpha + (dalpha * (alpha ^ 0xFF) >> 8);
-		    *dstp = d1 | d | (dalpha << 24);
-		  }
-		}
-		++srcp;
-		++dstp;
-	    }, width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4({
+        Uint32 dalpha;
+        Uint32 d;
+        Uint32 s1;
+        Uint32 d1;
+        Uint32 s = *srcp;
+        Uint32 alpha = s >> 24;
+        /* FIXME: Here we special-case opaque alpha since the
+           compositioning used (>>8 instead of /255) doesn't handle
+           it correctly. Also special-case alpha=0 for speed?
+           Benchmark this! */
+        if (alpha) {
+          if (alpha == SDL_ALPHA_OPAQUE) {
+              *dstp = *srcp;
+          } else {
+            /*
+             * take out the middle component (green), and process
+             * the other two in parallel. One multiply less.
+             */
+            d = *dstp;
+            dalpha = d >> 24;
+            s1 = s & 0xff00ff;
+            d1 = d & 0xff00ff;
+            d1 = (d1 + ((s1 - d1) * alpha >> 8)) & 0xff00ff;
+            s &= 0xff00;
+            d &= 0xff00;
+            d = (d + ((s - d) * alpha >> 8)) & 0xff00;
+            dalpha = alpha + (dalpha * (alpha ^ 0xFF) >> 8);
+            *dstp = d1 | d | (dalpha << 24);
+          }
+        }
+        ++srcp;
+        ++dstp;
+        }, width);
+        /* *INDENT-ON* */
         srcp += srcskip;
         dstp += dstskip;
     }
@@ -533,47 +533,47 @@
     multmask2 = 0x00FF00FF00FF00FFULL;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4({
-		Uint32 alpha;
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4({
+        Uint32 alpha;
 
-		_m_prefetch(srcp + 16);
-		_m_prefetch(dstp + 16);
+        _m_prefetch(srcp + 16);
+        _m_prefetch(dstp + 16);
 
-		alpha = *srcp & amask;
-		if (alpha == 0) {
-			/* do nothing */
-		} else if (alpha == amask) {
-			*dstp = *srcp;
-		} else {
-			src1 = _mm_cvtsi32_si64(*srcp); /* src(ARGB) -> src1 (0000ARGB) */
-			src1 = _mm_unpacklo_pi8(src1, mm_zero); /* 0A0R0G0B -> src1 */
+        alpha = *srcp & amask;
+        if (alpha == 0) {
+            /* do nothing */
+        } else if (alpha == amask) {
+            *dstp = *srcp;
+        } else {
+            src1 = _mm_cvtsi32_si64(*srcp); /* src(ARGB) -> src1 (0000ARGB) */
+            src1 = _mm_unpacklo_pi8(src1, mm_zero); /* 0A0R0G0B -> src1 */
 
-			dst1 = _mm_cvtsi32_si64(*dstp); /* dst(ARGB) -> dst1 (0000ARGB) */
-			dst1 = _mm_unpacklo_pi8(dst1, mm_zero); /* 0A0R0G0B -> dst1 */
+            dst1 = _mm_cvtsi32_si64(*dstp); /* dst(ARGB) -> dst1 (0000ARGB) */
+            dst1 = _mm_unpacklo_pi8(dst1, mm_zero); /* 0A0R0G0B -> dst1 */
 
-			mm_alpha = _mm_cvtsi32_si64(alpha); /* alpha -> mm_alpha (0000000A) */
-			mm_alpha = _mm_srli_si64(mm_alpha, ashift); /* mm_alpha >> ashift -> mm_alpha(0000000A) */
-			mm_alpha = _mm_unpacklo_pi16(mm_alpha, mm_alpha); /* 00000A0A -> mm_alpha */
-			mm_alpha2 = _mm_unpacklo_pi32(mm_alpha, mm_alpha); /* 0A0A0A0A -> mm_alpha2 */
-			mm_alpha = _mm_or_si64(mm_alpha2, *(__m64 *) & multmask);	/* 0F0A0A0A -> mm_alpha */
-			mm_alpha2 = _mm_xor_si64(mm_alpha2, *(__m64 *) & multmask2);	/* 255 - mm_alpha -> mm_alpha */
+            mm_alpha = _mm_cvtsi32_si64(alpha); /* alpha -> mm_alpha (0000000A) */
+            mm_alpha = _mm_srli_si64(mm_alpha, ashift); /* mm_alpha >> ashift -> mm_alpha(0000000A) */
+            mm_alpha = _mm_unpacklo_pi16(mm_alpha, mm_alpha); /* 00000A0A -> mm_alpha */
+            mm_alpha2 = _mm_unpacklo_pi32(mm_alpha, mm_alpha); /* 0A0A0A0A -> mm_alpha2 */
+            mm_alpha = _mm_or_si64(mm_alpha2, *(__m64 *) & multmask);    /* 0F0A0A0A -> mm_alpha */
+            mm_alpha2 = _mm_xor_si64(mm_alpha2, *(__m64 *) & multmask2);    /* 255 - mm_alpha -> mm_alpha */
 
 
-			/* blend */		    
-			src1 = _mm_mullo_pi16(src1, mm_alpha);
-			src1 = _mm_srli_pi16(src1, 8);
-			dst1 = _mm_mullo_pi16(dst1, mm_alpha2);
-			dst1 = _mm_srli_pi16(dst1, 8);
-			dst1 = _mm_add_pi16(src1, dst1);
-			dst1 = _mm_packs_pu16(dst1, mm_zero);
-			
-			*dstp = _mm_cvtsi64_si32(dst1); /* dst1 -> pixel */
-		}
-		++srcp;
-		++dstp;
-	    }, width);
-	    /* *INDENT-ON* */
+            /* blend */            
+            src1 = _mm_mullo_pi16(src1, mm_alpha);
+            src1 = _mm_srli_pi16(src1, 8);
+            dst1 = _mm_mullo_pi16(dst1, mm_alpha2);
+            dst1 = _mm_srli_pi16(dst1, 8);
+            dst1 = _mm_add_pi16(src1, dst1);
+            dst1 = _mm_packs_pu16(dst1, mm_zero);
+            
+            *dstp = _mm_cvtsi64_si32(dst1); /* dst1 -> pixel */
+        }
+        ++srcp;
+        ++dstp;
+        }, width);
+        /* *INDENT-ON* */
         srcp += srcskip;
         dstp += dstskip;
     }
@@ -585,13 +585,13 @@
 /* 16bpp special case for per-surface alpha=50%: blend 2 pixels in parallel */
 
 /* blend a single 16 bit pixel at 50% */
-#define BLEND16_50(d, s, mask)						\
-	((((s & mask) + (d & mask)) >> 1) + (s & d & (~mask & 0xffff)))
+#define BLEND16_50(d, s, mask)                        \
+    ((((s & mask) + (d & mask)) >> 1) + (s & d & (~mask & 0xffff)))
 
 /* blend two 16 bit pixels at 50% */
-#define BLEND2x16_50(d, s, mask)					     \
-	(((s & (mask | mask << 16)) >> 1) + ((d & (mask | mask << 16)) >> 1) \
-	 + (s & d & (~(mask | mask << 16))))
+#define BLEND2x16_50(d, s, mask)                         \
+    (((s & (mask | mask << 16)) >> 1) + ((d & (mask | mask << 16)) >> 1) \
+     + (s & d & (~(mask | mask << 16))))
 
 static void
 Blit16to16SurfaceAlpha128(SDL_BlitInfo * info, Uint16 mask)
@@ -727,103 +727,103 @@
         bmask = _mm_set_pi32(0x001F001F, 0x001F001F);   /* MASKBLUE -> bmask */
 
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP_124(
-			{
-				s = *srcp++;
-				d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x07e0f81f;
-				d = (d | d << 16) & 0x07e0f81f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x07e0f81f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			},{
-				s = *srcp++;
-				d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x07e0f81f;
-				d = (d | d << 16) & 0x07e0f81f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x07e0f81f;
-				*dstp++ = (Uint16)(d | d >> 16);
-				s = *srcp++;
-				d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x07e0f81f;
-				d = (d | d << 16) & 0x07e0f81f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x07e0f81f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			},{
-				src1 = *(__m64*)srcp; /* 4 src pixels -> src1 */
-				dst1 = *(__m64*)dstp; /* 4 dst pixels -> dst1 */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP_124(
+            {
+                s = *srcp++;
+                d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x07e0f81f;
+                d = (d | d << 16) & 0x07e0f81f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x07e0f81f;
+                *dstp++ = (Uint16)(d | d >> 16);
+            },{
+                s = *srcp++;
+                d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x07e0f81f;
+                d = (d | d << 16) & 0x07e0f81f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x07e0f81f;
+                *dstp++ = (Uint16)(d | d >> 16);
+                s = *srcp++;
+                d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x07e0f81f;
+                d = (d | d << 16) & 0x07e0f81f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x07e0f81f;
+                *dstp++ = (Uint16)(d | d >> 16);
+            },{
+                src1 = *(__m64*)srcp; /* 4 src pixels -> src1 */
+                dst1 = *(__m64*)dstp; /* 4 dst pixels -> dst1 */
 
-				/* red */
-				src2 = src1;
-				src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 [000r 000r 000r 000r] */
+                /* red */
+                src2 = src1;
+                src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 [000r 000r 000r 000r] */
 
-				dst2 = dst1;
-				dst2 = _mm_srli_pi16(dst2, 11); /* dst2 >> 11 -> dst2 [000r 000r 000r 000r] */
+                dst2 = dst1;
+                dst2 = _mm_srli_pi16(dst2, 11); /* dst2 >> 11 -> dst2 [000r 000r 000r 000r] */
 
-				/* blend */
-				src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
-				src2 = _mm_mullo_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
-				src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 */
-				dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
-				dst2 = _mm_slli_pi16(dst2, 11); /* dst2 << 11 -> dst2 */
+                /* blend */
+                src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
+                src2 = _mm_mullo_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
+                src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 */
+                dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
+                dst2 = _mm_slli_pi16(dst2, 11); /* dst2 << 11 -> dst2 */
 
-				mm_res = dst2; /* RED -> mm_res */
+                mm_res = dst2; /* RED -> mm_res */
 
-				/* green -- process the bits in place */
-				src2 = src1;
-				src2 = _mm_and_si64(src2, gmask); /* src & MASKGREEN -> src2 */
+                /* green -- process the bits in place */
+                src2 = src1;
+                src2 = _mm_and_si64(src2, gmask); /* src & MASKGREEN -> src2 */
 
-				dst2 = dst1;
-				dst2 = _mm_and_si64(dst2, gmask); /* dst & MASKGREEN -> dst2 */
+                dst2 = dst1;
+                dst2 = _mm_and_si64(dst2, gmask); /* dst & MASKGREEN -> dst2 */
 
-				/* blend */
-				src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
-				src2 = _mm_mulhi_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
-				src2 = _mm_slli_pi16(src2, 5); /* src2 << 5 -> src2 */
-				dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
+                /* blend */
+                src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
+                src2 = _mm_mulhi_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
+                src2 = _mm_slli_pi16(src2, 5); /* src2 << 5 -> src2 */
+                dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
 
-				mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN -> mm_res */
+                mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN -> mm_res */
 
-				/* blue */
-				src2 = src1;
-				src2 = _mm_and_si64(src2, bmask); /* src & MASKBLUE -> src2[000b 000b 000b 000b] */
+                /* blue */
+                src2 = src1;
+                src2 = _mm_and_si64(src2, bmask); /* src & MASKBLUE -> src2[000b 000b 000b 000b] */
 
-				dst2 = dst1;
-				dst2 = _mm_and_si64(dst2, bmask); /* dst & MASKBLUE -> dst2[000b 000b 000b 000b] */
+                dst2 = dst1;
+                dst2 = _mm_and_si64(dst2, bmask); /* dst & MASKBLUE -> dst2[000b 000b 000b 000b] */
 
-				/* blend */
-				src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
-				src2 = _mm_mullo_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
-				src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 */
-				dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
-				dst2 = _mm_and_si64(dst2, bmask); /* dst2 & MASKBLUE -> dst2 */
+                /* blend */
+                src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
+                src2 = _mm_mullo_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
+                src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 */
+                dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
+                dst2 = _mm_and_si64(dst2, bmask); /* dst2 & MASKBLUE -> dst2 */
 
-				mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN | BLUE -> mm_res */
+                mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN | BLUE -> mm_res */
 
-				*(__m64*)dstp = mm_res; /* mm_res -> 4 dst pixels */
+                *(__m64*)dstp = mm_res; /* mm_res -> 4 dst pixels */
 
-				srcp += 4;
-				dstp += 4;
-			}, width);
-			/* *INDENT-ON* */
+                srcp += 4;
+                dstp += 4;
+            }, width);
+            /* *INDENT-ON* */
             srcp += srcskip;
             dstp += dstskip;
         }
@@ -865,103 +865,103 @@
         bmask = _mm_set_pi32(0x001F001F, 0x001F001F);   /* MASKBLUE -> bmask */
 
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP_124(
-			{
-				s = *srcp++;
-				d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x03e07c1f;
-				d = (d | d << 16) & 0x03e07c1f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x03e07c1f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			},{
-				s = *srcp++;
-				d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x03e07c1f;
-				d = (d | d << 16) & 0x03e07c1f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x03e07c1f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			        s = *srcp++;
-				d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x03e07c1f;
-				d = (d | d << 16) & 0x03e07c1f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x03e07c1f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			},{
-				src1 = *(__m64*)srcp; /* 4 src pixels -> src1 */
-				dst1 = *(__m64*)dstp; /* 4 dst pixels -> dst1 */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP_124(
+            {
+                s = *srcp++;
+                d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x03e07c1f;
+                d = (d | d << 16) & 0x03e07c1f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x03e07c1f;
+                *dstp++ = (Uint16)(d | d >> 16);
+            },{
+                s = *srcp++;
+                d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x03e07c1f;
+                d = (d | d << 16) & 0x03e07c1f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x03e07c1f;
+                *dstp++ = (Uint16)(d | d >> 16);
+                    s = *srcp++;
+                d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x03e07c1f;
+                d = (d | d << 16) & 0x03e07c1f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x03e07c1f;
+                *dstp++ = (Uint16)(d | d >> 16);
+            },{
+                src1 = *(__m64*)srcp; /* 4 src pixels -> src1 */
+                dst1 = *(__m64*)dstp; /* 4 dst pixels -> dst1 */
 
-				/* red -- process the bits in place */
-				src2 = src1;
-				src2 = _mm_and_si64(src2, rmask); /* src & MASKRED -> src2 */
+                /* red -- process the bits in place */
+                src2 = src1;
+                src2 = _mm_and_si64(src2, rmask); /* src & MASKRED -> src2 */
 
-				dst2 = dst1;
-				dst2 = _mm_and_si64(dst2, rmask); /* dst & MASKRED -> dst2 */
+                dst2 = dst1;
+                dst2 = _mm_and_si64(dst2, rmask); /* dst & MASKRED -> dst2 */
 
-				/* blend */
-				src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
-				src2 = _mm_mulhi_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
-				src2 = _mm_slli_pi16(src2, 5); /* src2 << 5 -> src2 */
-				dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
-				dst2 = _mm_and_si64(dst2, rmask); /* dst2 & MASKRED -> dst2 */
+                /* blend */
+                src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
+                src2 = _mm_mulhi_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
+                src2 = _mm_slli_pi16(src2, 5); /* src2 << 5 -> src2 */
+                dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
+                dst2 = _mm_and_si64(dst2, rmask); /* dst2 & MASKRED -> dst2 */
 
-				mm_res = dst2; /* RED -> mm_res */
-				
-				/* green -- process the bits in place */
-				src2 = src1;
-				src2 = _mm_and_si64(src2, gmask); /* src & MASKGREEN -> src2 */
+                mm_res = dst2; /* RED -> mm_res */
+                
+                /* green -- process the bits in place */
+                src2 = src1;
+                src2 = _mm_and_si64(src2, gmask); /* src & MASKGREEN -> src2 */
 
-				dst2 = dst1;
-				dst2 = _mm_and_si64(dst2, gmask); /* dst & MASKGREEN -> dst2 */
+                dst2 = dst1;
+                dst2 = _mm_and_si64(dst2, gmask); /* dst & MASKGREEN -> dst2 */
 
-				/* blend */
-				src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
-				src2 = _mm_mulhi_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
-				src2 = _mm_slli_pi16(src2, 5); /* src2 << 5 -> src2 */
-				dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
+                /* blend */
+                src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
+                src2 = _mm_mulhi_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
+                src2 = _mm_slli_pi16(src2, 5); /* src2 << 5 -> src2 */
+                dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
 
-				mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN -> mm_res */
+                mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN -> mm_res */
 
-				/* blue */
-				src2 = src1; /* src -> src2 */
-				src2 = _mm_and_si64(src2, bmask); /* src & MASKBLUE -> src2[000b 000b 000b 000b] */
+                /* blue */
+                src2 = src1; /* src -> src2 */
+                src2 = _mm_and_si64(src2, bmask); /* src & MASKBLUE -> src2[000b 000b 000b 000b] */
 
-				dst2 = dst1; /* dst -> dst2 */
-				dst2 = _mm_and_si64(dst2, bmask); /* dst & MASKBLUE -> dst2[000b 000b 000b 000b] */
+                dst2 = dst1; /* dst -> dst2 */
+                dst2 = _mm_and_si64(dst2, bmask); /* dst & MASKBLUE -> dst2[000b 000b 000b 000b] */
 
-				/* blend */
-				src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
-				src2 = _mm_mullo_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
-				src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 */
-				dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
-				dst2 = _mm_and_si64(dst2, bmask); /* dst2 & MASKBLUE -> dst2 */
+                /* blend */
+                src2 = _mm_sub_pi16(src2, dst2);/* src - dst -> src2 */
+                src2 = _mm_mullo_pi16(src2, mm_alpha); /* src2 * alpha -> src2 */
+                src2 = _mm_srli_pi16(src2, 11); /* src2 >> 11 -> src2 */
+                dst2 = _mm_add_pi16(src2, dst2); /* src2 + dst2 -> dst2 */
+                dst2 = _mm_and_si64(dst2, bmask); /* dst2 & MASKBLUE -> dst2 */
 
-				mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN | BLUE -> mm_res */
+                mm_res = _mm_or_si64(mm_res, dst2); /* RED | GREEN | BLUE -> mm_res */
 
-				*(__m64*)dstp = mm_res; /* mm_res -> 4 dst pixels */
+                *(__m64*)dstp = mm_res; /* mm_res -> 4 dst pixels */
 
-				srcp += 4;
-				dstp += 4;
-			}, width);
-			/* *INDENT-ON* */
+                srcp += 4;
+                dstp += 4;
+            }, width);
+            /* *INDENT-ON* */
             srcp += srcskip;
             dstp += dstskip;
         }
@@ -988,22 +988,22 @@
         alpha >>= 3;            /* downscale alpha to 5 bits */
 
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP4({
-				Uint32 s = *srcp++;
-				Uint32 d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x07e0f81f;
-				d = (d | d << 16) & 0x07e0f81f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x07e0f81f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			}, width);
-			/* *INDENT-ON* */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP4({
+                Uint32 s = *srcp++;
+                Uint32 d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x07e0f81f;
+                d = (d | d << 16) & 0x07e0f81f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x07e0f81f;
+                *dstp++ = (Uint16)(d | d >> 16);
+            }, width);
+            /* *INDENT-ON* */
             srcp += srcskip;
             dstp += dstskip;
         }
@@ -1027,22 +1027,22 @@
         alpha >>= 3;            /* downscale alpha to 5 bits */
 
         while (height--) {
-			/* *INDENT-OFF* */
-			DUFFS_LOOP4({
-				Uint32 s = *srcp++;
-				Uint32 d = *dstp;
-				/*
-				 * shift out the middle component (green) to
-				 * the high 16 bits, and process all three RGB
-				 * components at the same time.
-				 */
-				s = (s | s << 16) & 0x03e07c1f;
-				d = (d | d << 16) & 0x03e07c1f;
-				d += (s - d) * alpha >> 5;
-				d &= 0x03e07c1f;
-				*dstp++ = (Uint16)(d | d >> 16);
-			}, width);
-			/* *INDENT-ON* */
+            /* *INDENT-OFF* */
+            DUFFS_LOOP4({
+                Uint32 s = *srcp++;
+                Uint32 d = *dstp;
+                /*
+                 * shift out the middle component (green) to
+                 * the high 16 bits, and process all three RGB
+                 * components at the same time.
+                 */
+                s = (s | s << 16) & 0x03e07c1f;
+                d = (d | d << 16) & 0x03e07c1f;
+                d += (s - d) * alpha >> 5;
+                d &= 0x03e07c1f;
+                *dstp++ = (Uint16)(d | d >> 16);
+            }, width);
+            /* *INDENT-ON* */
             srcp += srcskip;
             dstp += dstskip;
         }
@@ -1061,35 +1061,35 @@
     int dstskip = info->dst_skip >> 1;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4({
-		Uint32 s = *srcp;
-		unsigned alpha = s >> 27; /* downscale alpha to 5 bits */
-		/* FIXME: Here we special-case opaque alpha since the
-		   compositioning used (>>8 instead of /255) doesn't handle
-		   it correctly. Also special-case alpha=0 for speed?
-		   Benchmark this! */
-		if(alpha) {   
-		  if(alpha == (SDL_ALPHA_OPAQUE >> 3)) {
-		    *dstp = (Uint16)((s >> 8 & 0xf800) + (s >> 5 & 0x7e0) + (s >> 3  & 0x1f));
-		  } else {
-		    Uint32 d = *dstp;
-		    /*
-		     * convert source and destination to G0RAB65565
-		     * and blend all components at the same time
-		     */
-		    s = ((s & 0xfc00) << 11) + (s >> 8 & 0xf800)
-		      + (s >> 3 & 0x1f);
-		    d = (d | d << 16) & 0x07e0f81f;
-		    d += (s - d) * alpha >> 5;
-		    d &= 0x07e0f81f;
-		    *dstp = (Uint16)(d | d >> 16);
-		  }
-		}
-		srcp++;
-		dstp++;
-	    }, width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4({
+        Uint32 s = *srcp;
+        unsigned alpha = s >> 27; /* downscale alpha to 5 bits */
+        /* FIXME: Here we special-case opaque alpha since the
+           compositioning used (>>8 instead of /255) doesn't handle
+           it correctly. Also special-case alpha=0 for speed?
+           Benchmark this! */
+        if(alpha) {   
+          if(alpha == (SDL_ALPHA_OPAQUE >> 3)) {
+            *dstp = (Uint16)((s >> 8 & 0xf800) + (s >> 5 & 0x7e0) + (s >> 3  & 0x1f));
+          } else {
+            Uint32 d = *dstp;
+            /*
+             * convert source and destination to G0RAB65565
+             * and blend all components at the same time
+             */
+            s = ((s & 0xfc00) << 11) + (s >> 8 & 0xf800)
+              + (s >> 3 & 0x1f);
+            d = (d | d << 16) & 0x07e0f81f;
+            d += (s - d) * alpha >> 5;
+            d &= 0x07e0f81f;
+            *dstp = (Uint16)(d | d >> 16);
+          }
+        }
+        srcp++;
+        dstp++;
+        }, width);
+        /* *INDENT-ON* */
         srcp += srcskip;
         dstp += dstskip;
     }
@@ -1107,36 +1107,36 @@
     int dstskip = info->dst_skip >> 1;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4({
-		unsigned alpha;
-		Uint32 s = *srcp;
-		alpha = s >> 27; /* downscale alpha to 5 bits */
-		/* FIXME: Here we special-case opaque alpha since the
-		   compositioning used (>>8 instead of /255) doesn't handle
-		   it correctly. Also special-case alpha=0 for speed?
-		   Benchmark this! */
-		if(alpha) {   
-		  if(alpha == (SDL_ALPHA_OPAQUE >> 3)) {
-		    *dstp = (Uint16)((s >> 9 & 0x7c00) + (s >> 6 & 0x3e0) + (s >> 3  & 0x1f));
-		  } else {
-		    Uint32 d = *dstp;
-		    /*
-		     * convert source and destination to G0RAB65565
-		     * and blend all components at the same time
-		     */
-		    s = ((s & 0xf800) << 10) + (s >> 9 & 0x7c00)
-		      + (s >> 3 & 0x1f);
-		    d = (d | d << 16) & 0x03e07c1f;
-		    d += (s - d) * alpha >> 5;
-		    d &= 0x03e07c1f;
-		    *dstp = (Uint16)(d | d >> 16);
-		  }
-		}
-		srcp++;
-		dstp++;
-	    }, width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4({
+        unsigned alpha;
+        Uint32 s = *srcp;
+        alpha = s >> 27; /* downscale alpha to 5 bits */
+        /* FIXME: Here we special-case opaque alpha since the
+           compositioning used (>>8 instead of /255) doesn't handle
+           it correctly. Also special-case alpha=0 for speed?
+           Benchmark this! */
+        if(alpha) {   
+          if(alpha == (SDL_ALPHA_OPAQUE >> 3)) {
+            *dstp = (Uint16)((s >> 9 & 0x7c00) + (s >> 6 & 0x3e0) + (s >> 3  & 0x1f));
+          } else {
+            Uint32 d = *dstp;
+            /*
+             * convert source and destination to G0RAB65565
+             * and blend all components at the same time
+             */
+            s = ((s & 0xf800) << 10) + (s >> 9 & 0x7c00)
+              + (s >> 3 & 0x1f);
+            d = (d | d << 16) & 0x03e07c1f;
+            d += (s - d) * alpha >> 5;
+            d &= 0x03e07c1f;
+            *dstp = (Uint16)(d | d >> 16);
+          }
+        }
+        srcp++;
+        dstp++;
+        }, width);
+        /* *INDENT-ON* */
         srcp += srcskip;
         dstp += dstskip;
     }
@@ -1163,18 +1163,18 @@
 
     if (sA) {
         while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4(
-	    {
-		DISEMBLE_RGB(src, srcbpp, srcfmt, Pixel, sR, sG, sB);
-		DISEMBLE_RGBA(dst, dstbpp, dstfmt, Pixel, dR, dG, dB, dA);
-		ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
-		ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
-		src += srcbpp;
-		dst += dstbpp;
-	    },
-	    width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4(
+        {
+        DISEMBLE_RGB(src, srcbpp, srcfmt, Pixel, sR, sG, sB);
+        DISEMBLE_RGBA(dst, dstbpp, dstfmt, Pixel, dR, dG, dB, dA);
+        ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
+        ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
+        src += srcbpp;
+        dst += dstbpp;
+        },
+        width);
+        /* *INDENT-ON* */
             src += srcskip;
             dst += dstskip;
         }
@@ -1202,21 +1202,21 @@
     const unsigned sA = info->a;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4(
-	    {
-		RETRIEVE_RGB_PIXEL(src, srcbpp, Pixel);
-		if(sA && Pixel != ckey) {
-		    RGB_FROM_PIXEL(Pixel, srcfmt, sR, sG, sB);
-		    DISEMBLE_RGBA(dst, dstbpp, dstfmt, Pixel, dR, dG, dB, dA);
-		    ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
-		    ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
-		}
-		src += srcbpp;
-		dst += dstbpp;
-	    },
-	    width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4(
+        {
+        RETRIEVE_RGB_PIXEL(src, srcbpp, Pixel);
+        if(sA && Pixel != ckey) {
+            RGB_FROM_PIXEL(Pixel, srcfmt, sR, sG, sB);
+            DISEMBLE_RGBA(dst, dstbpp, dstfmt, Pixel, dR, dG, dB, dA);
+            ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
+            ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
+        }
+        src += srcbpp;
+        dst += dstbpp;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
@@ -1245,20 +1245,20 @@
     dstbpp = dstfmt->BytesPerPixel;
 
     while (height--) {
-	    /* *INDENT-OFF* */
-	    DUFFS_LOOP4(
-	    {
-		DISEMBLE_RGBA(src, srcbpp, srcfmt, Pixel, sR, sG, sB, sA);
-		if(sA) {
-		    DISEMBLE_RGBA(dst, dstbpp, dstfmt, Pixel, dR, dG, dB, dA);
-		    ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
-		    ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
-		}
-		src += srcbpp;
-		dst += dstbpp;
-	    },
-	    width);
-	    /* *INDENT-ON* */
+        /* *INDENT-OFF* */
+        DUFFS_LOOP4(
+        {
+        DISEMBLE_RGBA(src, srcbpp, srcfmt, Pixel, sR, sG, sB, sA);
+        if(sA) {
+            DISEMBLE_RGBA(dst, dstbpp, dstfmt, Pixel, dR, dG, dB, dA);
+            ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
+            ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
+        }
+        src += srcbpp;
+        dst += dstbpp;
+        },
+        width);
+        /* *INDENT-ON* */
         src += srcskip;
         dst += dstskip;
     }
diff --git a/source/src/video/SDL_blit_N.c b/source/src/video/SDL_blit_N.c
index 441cd9a..d6ec417 100644
--- a/source/src/video/SDL_blit_N.c
+++ b/source/src/video/SDL_blit_N.c
@@ -2333,6 +2333,31 @@
     /* Set up some basic variables */
     ckey &= rgbmask;
 
+    /* Fastpath: same source/destination format, no Amask, bpp 32, loop is vectorized. ~10x faster */
+    if (srcfmt->format == dstfmt->format &&
+        (srcfmt->format == SDL_PIXELFORMAT_RGB888 || srcfmt->format == SDL_PIXELFORMAT_BGR888)) {
+        Uint32 *src32 = (Uint32*)src;
+        Uint32 *dst32 = (Uint32*)dst;
+        srcskip /= sizeof(Uint32);
+        dstskip /= sizeof(Uint32);
+        while (height--) {
+            /* *INDENT-OFF* */
+            DUFFS_LOOP(
+            {
+                if (*src32 != ckey) {
+                    *dst32 = *src32;
+                }
+                ++src32;
+                ++dst32;
+            },
+            width);
+            /* *INDENT-ON* */
+            src32 += srcskip;
+            dst32 += dstskip;
+        }
+        return;
+    }
+
     while (height--) {
         /* *INDENT-OFF* */
         DUFFS_LOOP(
@@ -2380,6 +2405,34 @@
     dstbpp = dstfmt->BytesPerPixel;
     ckey &= rgbmask;
 
+    /* Fastpath: same source/destination format, with Amask, bpp 32, loop is vectorized. ~10x faster */
+    if (srcfmt->format == dstfmt->format &&
+        (srcfmt->format == SDL_PIXELFORMAT_ARGB8888 ||
+         srcfmt->format == SDL_PIXELFORMAT_ABGR8888 ||
+         srcfmt->format == SDL_PIXELFORMAT_BGRA8888 ||
+         srcfmt->format == SDL_PIXELFORMAT_RGBA8888)) {
+        Uint32 *src32 = (Uint32*)src;
+        Uint32 *dst32 = (Uint32*)dst;
+        srcskip /= sizeof(Uint32);
+        dstskip /= sizeof(Uint32);
+        while (height--) {
+            /* *INDENT-OFF* */
+            DUFFS_LOOP(
+            {
+                if ((*src32 & rgbmask) != ckey) {
+                    *dst32 = *src32;
+                }
+                ++src32;
+                ++dst32;
+            },
+            width);
+            /* *INDENT-ON* */
+            src32 += srcskip;
+            dst32 += dstskip;
+        }
+        return;
+    }
+
     while (height--) {
         /* *INDENT-OFF* */
         DUFFS_LOOP(
diff --git a/source/src/video/SDL_blit_copy.h b/source/src/video/SDL_blit_copy.h
index 4665179..d569ae0 100644
--- a/source/src/video/SDL_blit_copy.h
+++ b/source/src/video/SDL_blit_copy.h
@@ -19,6 +19,11 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_blit_copy_h_
+#define SDL_blit_copy_h_
+
 void SDL_BlitCopy(SDL_BlitInfo * info);
 
+#endif /* SDL_blit_copy_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_blit_slow.h b/source/src/video/SDL_blit_slow.h
index 02d360a..d27fcd2 100644
--- a/source/src/video/SDL_blit_slow.h
+++ b/source/src/video/SDL_blit_slow.h
@@ -18,8 +18,14 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_blit_slow_h_
+#define SDL_blit_slow_h_
+
 #include "../SDL_internal.h"
 
 extern void SDL_Blit_Slow(SDL_BlitInfo * info);
 
+#endif /* SDL_blit_slow_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_egl.c b/source/src/video/SDL_egl.c
index 0daf98a..78abc03 100644
--- a/source/src/video/SDL_egl.c
+++ b/source/src/video/SDL_egl.c
@@ -406,6 +406,9 @@
         }
     }
 
+    _this->egl_data->egl_version_major = egl_version_major;
+    _this->egl_data->egl_version_minor = egl_version_minor;
+
     if (egl_version_major == 1 && egl_version_minor == 5) {
         LOAD_FUNC(eglGetPlatformDisplay);
     }
@@ -445,6 +448,64 @@
     
     return 0;
 }
+
+#ifdef DUMP_EGL_CONFIG
+
+#define ATTRIBUTE(_attr) { _attr, #_attr }
+
+typedef struct {
+    EGLint attribute;
+    char const* name;
+} Attribute;
+
+Attribute attributes[] = {
+        ATTRIBUTE( EGL_BUFFER_SIZE ),
+        ATTRIBUTE( EGL_ALPHA_SIZE ),
+        ATTRIBUTE( EGL_BLUE_SIZE ),
+        ATTRIBUTE( EGL_GREEN_SIZE ),
+        ATTRIBUTE( EGL_RED_SIZE ),
+        ATTRIBUTE( EGL_DEPTH_SIZE ),
+        ATTRIBUTE( EGL_STENCIL_SIZE ),
+        ATTRIBUTE( EGL_CONFIG_CAVEAT ),
+        ATTRIBUTE( EGL_CONFIG_ID ),
+        ATTRIBUTE( EGL_LEVEL ),
+        ATTRIBUTE( EGL_MAX_PBUFFER_HEIGHT ),
+        ATTRIBUTE( EGL_MAX_PBUFFER_WIDTH ),
+        ATTRIBUTE( EGL_MAX_PBUFFER_PIXELS ),
+        ATTRIBUTE( EGL_NATIVE_RENDERABLE ),
+        ATTRIBUTE( EGL_NATIVE_VISUAL_ID ),
+        ATTRIBUTE( EGL_NATIVE_VISUAL_TYPE ),
+        ATTRIBUTE( EGL_SAMPLES ),
+        ATTRIBUTE( EGL_SAMPLE_BUFFERS ),
+        ATTRIBUTE( EGL_SURFACE_TYPE ),
+        ATTRIBUTE( EGL_TRANSPARENT_TYPE ),
+        ATTRIBUTE( EGL_TRANSPARENT_BLUE_VALUE ),
+        ATTRIBUTE( EGL_TRANSPARENT_GREEN_VALUE ),
+        ATTRIBUTE( EGL_TRANSPARENT_RED_VALUE ),
+        ATTRIBUTE( EGL_BIND_TO_TEXTURE_RGB ),
+        ATTRIBUTE( EGL_BIND_TO_TEXTURE_RGBA ),
+        ATTRIBUTE( EGL_MIN_SWAP_INTERVAL ),
+        ATTRIBUTE( EGL_MAX_SWAP_INTERVAL ),
+        ATTRIBUTE( EGL_LUMINANCE_SIZE ),
+        ATTRIBUTE( EGL_ALPHA_MASK_SIZE ),
+        ATTRIBUTE( EGL_COLOR_BUFFER_TYPE ),
+        ATTRIBUTE( EGL_RENDERABLE_TYPE ),
+        ATTRIBUTE( EGL_MATCH_NATIVE_PIXMAP ),
+        ATTRIBUTE( EGL_CONFORMANT ),
+};
+
+
+static void dumpconfig(_THIS, EGLConfig config)
+{
+    int attr;
+    for (attr = 0 ; attr<sizeof(attributes)/sizeof(Attribute) ; attr++) {
+        EGLint value;
+        _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, config, attributes[attr].attribute, &value);
+        SDL_Log("\t%-32s: %10d (0x%08x)\n", attributes[attr].name, value, value);
+    }
+}
+
+#endif /* DUMP_EGL_CONFIG */
 
 int
 SDL_EGL_ChooseConfig(_THIS) 
@@ -570,6 +631,10 @@
             break; /* we found an exact match! */
         }
     }
+
+#ifdef DUMP_EGL_CONFIG
+    dumpconfig(_this, _this->egl_data->egl_config);
+#endif
     
     return 0;
 }
@@ -595,6 +660,24 @@
     if (_this->gl_config.share_with_current_context) {
         share_context = (EGLContext)SDL_GL_GetCurrentContext();
     }
+
+#if SDL_VIDEO_DRIVER_ANDROID
+    if ((_this->gl_config.flags & SDL_GL_CONTEXT_DEBUG_FLAG) != 0) {
+        /* If SDL_GL_CONTEXT_DEBUG_FLAG is set but EGL_KHR_debug unsupported, unset.
+         * This is required because some Android devices like to complain about it
+         * by "silently" failing, logging a hint which could be easily overlooked:
+         * E/libEGL  (26984): validate_display:255 error 3008 (EGL_BAD_DISPLAY)
+         * The following explicitly checks for EGL_KHR_debug before EGL 1.5
+         */
+        int egl_version_major = _this->egl_data->egl_version_major;
+        int egl_version_minor = _this->egl_data->egl_version_minor;
+        if (((egl_version_major < 1) || (egl_version_major == 1 && egl_version_minor < 5)) &&
+            !SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_debug")) {
+            /* SDL profile bits match EGL profile bits. */
+            _this->gl_config.flags &= ~SDL_GL_CONTEXT_DEBUG_FLAG;
+        }
+    }
+#endif
 
     /* Set the context version and other attributes. */
     if ((major_version < 3 || (minor_version == 0 && profile_es)) &&
@@ -763,7 +846,6 @@
     }
     
     if (egl_context != NULL && egl_context != EGL_NO_CONTEXT) {
-        SDL_EGL_MakeCurrent(_this, NULL, NULL);
         _this->egl_data->eglDestroyContext(_this->egl_data->egl_display, egl_context);
     }
         
@@ -775,7 +857,7 @@
     /* max 2 values plus terminator. */
     EGLint attribs[3];
     int attr = 0;
-	
+
     EGLSurface * surface;
 
     if (SDL_EGL_ChooseConfig(_this) != 0) {
@@ -807,7 +889,7 @@
             return EGL_NO_SURFACE;
         }
     }
-	
+
     attribs[attr++] = EGL_NONE;
     
     surface = _this->egl_data->eglCreateWindowSurface(
diff --git a/source/src/video/SDL_egl_c.h b/source/src/video/SDL_egl_c.h
index 80b1319..d1c4129 100644
--- a/source/src/video/SDL_egl_c.h
+++ b/source/src/video/SDL_egl_c.h
@@ -36,6 +36,7 @@
     EGLConfig egl_config;
     int egl_swapinterval;
     int egl_surfacetype;
+    int egl_version_major, egl_version_minor;
     
     EGLDisplay(EGLAPIENTRY *eglGetDisplay) (NativeDisplayType display);
     EGLDisplay(EGLAPIENTRY *eglGetPlatformDisplay) (EGLenum platform,
diff --git a/source/src/video/SDL_pixels.c b/source/src/video/SDL_pixels.c
index 2e26395..c2e4163 100644
--- a/source/src/video/SDL_pixels.c
+++ b/source/src/video/SDL_pixels.c
@@ -326,7 +326,7 @@
         if (Rmask == 0) {
             return SDL_PIXELFORMAT_RGB555;
         }
-	/* fallthrough */
+    /* fallthrough */
     case 16:
         if (Rmask == 0) {
             return SDL_PIXELFORMAT_RGB565;
diff --git a/source/src/video/SDL_pixels_c.h b/source/src/video/SDL_pixels_c.h
index 900f0b0..c84e155 100644
--- a/source/src/video/SDL_pixels_c.h
+++ b/source/src/video/SDL_pixels_c.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_pixels_c_h_
+#define SDL_pixels_c_h_
+
 #include "../SDL_internal.h"
 
 /* Useful functions and variables from SDL_pixel.c */
@@ -37,4 +41,6 @@
 extern void SDL_DitherColors(SDL_Color * colors, int bpp);
 extern Uint8 SDL_FindColor(SDL_Palette * pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
 
+#endif /* SDL_pixels_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_rect_c.h b/source/src/video/SDL_rect_c.h
index d67e493..56d6f2e 100644
--- a/source/src/video/SDL_rect_c.h
+++ b/source/src/video/SDL_rect_c.h
@@ -18,8 +18,14 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_rect_c_h_
+#define SDL_rect_c_h_
+
 #include "../SDL_internal.h"
 
 extern SDL_bool SDL_GetSpanEnclosingRect(int width, int height, int numrects, const SDL_Rect * rects, SDL_Rect *span);
 
+#endif /* SDL_rect_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_shape_internals.h b/source/src/video/SDL_shape_internals.h
index 3af175c..49a8786 100644
--- a/source/src/video/SDL_shape_internals.h
+++ b/source/src/video/SDL_shape_internals.h
@@ -36,21 +36,21 @@
 #endif
 
 typedef struct {
-	struct SDL_ShapeTree *upleft,*upright,*downleft,*downright;
+    struct SDL_ShapeTree *upleft,*upright,*downleft,*downright;
 } SDL_QuadTreeChildren;
 
 typedef union {
-	SDL_QuadTreeChildren children;
-	SDL_Rect shape;
+    SDL_QuadTreeChildren children;
+    SDL_Rect shape;
 } SDL_ShapeUnion;
 
 typedef enum { QuadShape,TransparentShape,OpaqueShape } SDL_ShapeKind;
 
 typedef struct {
-	SDL_ShapeKind kind;
-	SDL_ShapeUnion data;
+    SDL_ShapeKind kind;
+    SDL_ShapeUnion data;
 } SDL_ShapeTree;
-	
+
 typedef void(*SDL_TraversalFunction)(SDL_ShapeTree*,void*);
 
 extern void SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface *shape,Uint8* bitmap,Uint8 ppb);
diff --git a/source/src/video/SDL_surface.c b/source/src/video/SDL_surface.c
index 719f831..1b2ee6c 100644
--- a/source/src/video/SDL_surface.c
+++ b/source/src/video/SDL_surface.c
@@ -37,7 +37,7 @@
 /*
  * Calculate the pad-aligned scanline width of a surface
  */
-int
+static int
 SDL_CalculatePitch(Uint32 format, int width)
 {
     int pitch;
@@ -292,6 +292,20 @@
     return 0;
 }
 
+SDL_bool
+SDL_HasColorKey(SDL_Surface * surface)
+{
+    if (!surface) {
+        return SDL_FALSE;
+    }
+
+    if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
+        return SDL_FALSE;
+    }
+
+	return SDL_TRUE;
+}
+
 int
 SDL_GetColorKey(SDL_Surface * surface, Uint32 * key)
 {
diff --git a/source/src/video/SDL_sysvideo.h b/source/src/video/SDL_sysvideo.h
index 9df71c9..25862ca 100644
--- a/source/src/video/SDL_sysvideo.h
+++ b/source/src/video/SDL_sysvideo.h
@@ -119,8 +119,8 @@
      !((W)->flags & SDL_WINDOW_MINIMIZED))
 
 /*
- * Define the SDL display structure This corresponds to physical monitors
- * attached to the system.
+ * Define the SDL display structure.
+ * This corresponds to physical monitors attached to the system.
  */
 struct SDL_VideoDisplay
 {
@@ -130,6 +130,7 @@
     SDL_DisplayMode *display_modes;
     SDL_DisplayMode desktop_mode;
     SDL_DisplayMode current_mode;
+    SDL_DisplayOrientation orientation;
 
     SDL_Window *fullscreen_window;
 
@@ -181,14 +182,14 @@
     int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);
 
     /*
-     * Get the dots/pixels-per-inch of a display
-     */
-    int (*GetDisplayDPI) (_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi);
-
-    /*
      * Get the usable bounds of a display (bounds minus menubar or whatever)
      */
     int (*GetDisplayUsableBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);
+
+    /*
+     * Get the dots/pixels-per-inch of a display
+     */
+    int (*GetDisplayDPI) (_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi);
 
     /*
      * Get a list of the available display modes for a display.
@@ -303,6 +304,9 @@
 
     /* Hit-testing */
     int (*SetWindowHitTest)(SDL_Window * window, SDL_bool enabled);
+
+    /* Tell window that app enabled drag'n'drop events */
+    void (*AcceptDragAndDrop)(SDL_Window * window, SDL_bool accept);
 
     /* * * */
     /* Data common to all drivers */
@@ -423,6 +427,8 @@
 extern int SDL_AddBasicVideoDisplay(const SDL_DisplayMode * desktop_mode);
 extern int SDL_AddVideoDisplay(const SDL_VideoDisplay * display);
 extern SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode * mode);
+extern int SDL_GetIndexOfDisplay(SDL_VideoDisplay *display);
+extern SDL_VideoDisplay *SDL_GetDisplay(int displayIndex);
 extern SDL_VideoDisplay *SDL_GetDisplayForWindow(SDL_Window *window);
 extern void *SDL_GetDisplayDriverData( int displayIndex );
 
@@ -454,6 +460,8 @@
 extern void SDL_OnApplicationWillEnterForeground(void);
 extern void SDL_OnApplicationDidBecomeActive(void);
 
+extern void SDL_ToggleDragAndDropSupport(void);
+
 #endif /* SDL_sysvideo_h_ */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/SDL_video.c b/source/src/video/SDL_video.c
index 8cf195d..336fdaa 100644
--- a/source/src/video/SDL_video.c
+++ b/source/src/video/SDL_video.c
@@ -641,7 +641,7 @@
     return _this->num_displays;
 }
 
-static int
+int
 SDL_GetIndexOfDisplay(SDL_VideoDisplay *display)
 {
     int displayIndex;
@@ -737,6 +737,17 @@
     }
 
     return -1;
+}
+
+SDL_DisplayOrientation
+SDL_GetDisplayOrientation(int displayIndex)
+{
+    SDL_VideoDisplay *display;
+
+    CHECK_DISPLAY_INDEX(displayIndex, SDL_ORIENTATION_UNKNOWN);
+
+    display = &_this->displays[displayIndex];
+    return display->orientation;
 }
 
 SDL_bool
@@ -1000,13 +1011,21 @@
 
     /* Actually change the display mode */
     if (!_this->SetDisplayMode) {
-        return SDL_SetError("Video driver doesn't support changing display mode");
+        return SDL_SetError("SDL video driver doesn't support changing display mode");
     }
     if (_this->SetDisplayMode(_this, display, &display_mode) < 0) {
         return -1;
     }
     display->current_mode = display_mode;
     return 0;
+}
+
+SDL_VideoDisplay *
+SDL_GetDisplay(int displayIndex)
+{
+    CHECK_DISPLAY_INDEX(displayIndex, NULL);
+
+    return &_this->displays[displayIndex];
 }
 
 int
@@ -1289,8 +1308,15 @@
 
                 /* Generate a mode change event here */
                 if (resized) {
+#ifndef ANDROID
+                    // Android may not resize the window to exactly what our fullscreen mode is, especially on
+                    // windowed Android environments like the Chromebook or Samsung DeX.  Given this, we shouldn't
+                    // use fullscreen_mode.w and fullscreen_mode.h, but rather get our current native size.  As such,
+                    // Android's SetWindowFullscreen will generate the window event for us with the proper final size.
+
                     SDL_SendWindowEvent(other, SDL_WINDOWEVENT_RESIZED,
                                         fullscreen_mode.w, fullscreen_mode.h);
+#endif
                 } else {
                     SDL_OnWindowResized(other);
                 }
@@ -1322,11 +1348,45 @@
 }
 
 #define CREATE_FLAGS \
-    (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN)
+    (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED)
+
+static SDL_INLINE SDL_bool
+IsAcceptingDragAndDrop(void)
+{
+    if ((SDL_GetEventState(SDL_DROPFILE) == SDL_ENABLE) ||
+        (SDL_GetEventState(SDL_DROPTEXT) == SDL_ENABLE)) {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
+}
+
+/* prepare a newly-created window */
+static SDL_INLINE void
+PrepareDragAndDropSupport(SDL_Window *window)
+{
+    if (_this->AcceptDragAndDrop) {
+        _this->AcceptDragAndDrop(window, IsAcceptingDragAndDrop());
+    }
+}
+
+/* toggle d'n'd for all existing windows. */
+void
+SDL_ToggleDragAndDropSupport(void)
+{
+    if (_this && _this->AcceptDragAndDrop) {
+        const SDL_bool enable = IsAcceptingDragAndDrop();
+        SDL_Window *window;
+        for (window = _this->windows; window; window = window->next) {
+            _this->AcceptDragAndDrop(window, enable);
+        }
+    }
+}
 
 static void
 SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
 {
+    PrepareDragAndDropSupport(window);
+
     if (flags & SDL_WINDOW_MAXIMIZED) {
         SDL_MaximizeWindow(window);
     }
@@ -1383,7 +1443,9 @@
 #endif
     if (flags & SDL_WINDOW_OPENGL) {
         if (!_this->GL_CreateContext) {
-            SDL_SetError("No OpenGL support in video driver");
+            SDL_SetError("OpenGL support is either not configured in SDL "
+                         "or not available in current SDL video driver "
+                         "(%s) or platform", _this->name);
             return NULL;
         }
         if (SDL_GL_LoadLibrary(NULL) < 0) {
@@ -1394,7 +1456,8 @@
     if (flags & SDL_WINDOW_VULKAN) {
         if (!_this->Vulkan_CreateSurface) {
             SDL_SetError("Vulkan support is either not configured in SDL "
-                         "or not available in video driver");
+                         "or not available in current SDL video driver "
+                         "(%s) or platform", _this->name);
             return NULL;
         }
         if (flags & SDL_WINDOW_OPENGL) {
@@ -1477,6 +1540,15 @@
         return NULL;
     }
 
+    /* Clear minimized if not on windows, only windows handles it at create rather than FinishWindowCreation,
+     * but it's important or window focus will get broken on windows!
+     */
+#if !defined(__WIN32__)
+    if (window->flags & SDL_WINDOW_MINIMIZED) {
+        window->flags &= ~SDL_WINDOW_MINIMIZED;
+    }
+#endif
+
 #if __WINRT__ && (NTDDI_VERSION < NTDDI_WIN10)
     /* HACK: WinRT 8.x apps can't choose whether or not they are fullscreen
        or not.  The user can choose this, via OS-provided UI, but this can't
@@ -1535,6 +1607,9 @@
         SDL_DestroyWindow(window);
         return NULL;
     }
+
+    PrepareDragAndDropSupport(window);
+
     return window;
 }
 
@@ -1544,7 +1619,9 @@
     SDL_bool loaded_opengl = SDL_FALSE;
 
     if ((flags & SDL_WINDOW_OPENGL) && !_this->GL_CreateContext) {
-        return SDL_SetError("No OpenGL support in video driver");
+        return SDL_SetError("OpenGL support is either not configured in SDL "
+                            "or not available in current SDL video driver "
+                            "(%s) or platform", _this->name);
     }
 
     if (window->flags & SDL_WINDOW_FOREIGN) {
@@ -2787,7 +2864,7 @@
         retval = 0;
     } else {
         if (!_this->GL_LoadLibrary) {
-            return SDL_SetError("No dynamic GL support in video driver");
+            return SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name);
         }
         retval = _this->GL_LoadLibrary(_this, path);
     }
@@ -2818,7 +2895,7 @@
             SDL_SetError("No GL driver has been loaded");
         }
     } else {
-        SDL_SetError("No dynamic GL support in video driver");
+        SDL_SetError("No dynamic GL support in current SDL video driver (%s)", _this->name);
     }
     return func;
 }
@@ -3774,6 +3851,8 @@
 
     if (!messageboxdata) {
         return SDL_InvalidParamError("messageboxdata");
+    } else if (messageboxdata->numbuttons < 0) {
+        return SDL_SetError("Invalid number of buttons");
     }
 
     current_window = SDL_GetKeyboardFocus();
@@ -3983,7 +4062,9 @@
         retval = 0;
     } else {
         if (!_this->Vulkan_LoadLibrary) {
-            return SDL_SetError("No Vulkan support in video driver");
+            return SDL_SetError("Vulkan support is either not configured in SDL "
+                                "or not available in current SDL video driver "
+                                "(%s) or platform", _this->name);
         }
         retval = _this->Vulkan_LoadLibrary(_this, path);
     }
@@ -4024,11 +4105,14 @@
 
 SDL_bool SDL_Vulkan_GetInstanceExtensions(SDL_Window *window, unsigned *count, const char **names)
 {
-    CHECK_WINDOW_MAGIC(window, SDL_FALSE);
+    if (window) {
+        CHECK_WINDOW_MAGIC(window, SDL_FALSE);
 
-    if (!(window->flags & SDL_WINDOW_VULKAN)) {
-        SDL_SetError(NOT_A_VULKAN_WINDOW);
-        return SDL_FALSE;
+        if (!(window->flags & SDL_WINDOW_VULKAN))
+        {
+            SDL_SetError(NOT_A_VULKAN_WINDOW);
+            return SDL_FALSE;
+        }
     }
 
     if (!count) {
diff --git a/source/src/video/SDL_vulkan_utils.c b/source/src/video/SDL_vulkan_utils.c
index d4cbed4..1b242f1 100644
--- a/source/src/video/SDL_vulkan_utils.c
+++ b/source/src/video/SDL_vulkan_utils.c
@@ -123,10 +123,10 @@
     {
         retval = SDL_calloc(1, sizeof(VkExtensionProperties)); // so we can return non-null
     }
-	else
-	{
-		retval = SDL_calloc(count, sizeof(VkExtensionProperties));
-	}
+    else
+    {
+        retval = SDL_calloc(count, sizeof(VkExtensionProperties));
+    }
     if(!retval)
     {
         SDL_OutOfMemory();
diff --git a/source/src/video/SDL_yuv.c b/source/src/video/SDL_yuv.c
index 50910a5..03b04dc 100644
--- a/source/src/video/SDL_yuv.c
+++ b/source/src/video/SDL_yuv.c
@@ -23,6 +23,7 @@
 #include "SDL_endian.h"
 #include "SDL_video.h"
 #include "SDL_pixels_c.h"
+#include "SDL_yuv_c.h"
 
 #include "yuv2rgb/yuv_rgb.h"
 
@@ -89,10 +90,10 @@
 }
 
 static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, int yuv_pitch,
-	                    const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride)
+                        const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride)
 {
-	const Uint8 *planes[3] = { NULL, NULL, NULL };
-	int pitches[3] = { 0, 0, 0 };
+    const Uint8 *planes[3] = { NULL, NULL, NULL };
+    int pitches[3] = { 0, 0, 0 };
 
     switch (format) {
     case SDL_PIXELFORMAT_YV12:
@@ -180,10 +181,10 @@
 
 static SDL_bool yuv_rgb_sse(
     Uint32 src_format, Uint32 dst_format,
-	Uint32 width, Uint32 height, 
-	const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride, 
-	Uint8 *rgb, Uint32 rgb_stride, 
-	YCbCrType yuv_type)
+    Uint32 width, Uint32 height, 
+    const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride, 
+    Uint8 *rgb, Uint32 rgb_stride, 
+    YCbCrType yuv_type)
 {
 #ifdef __SSE2__
     if (!SDL_HasSSE2()) {
@@ -289,10 +290,10 @@
 
 static SDL_bool yuv_rgb_std(
     Uint32 src_format, Uint32 dst_format,
-	Uint32 width, Uint32 height, 
-	const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride, 
-	Uint8 *rgb, Uint32 rgb_stride, 
-	YCbCrType yuv_type)
+    Uint32 width, Uint32 height, 
+    const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride, 
+    Uint8 *rgb, Uint32 rgb_stride, 
+    YCbCrType yuv_type)
 {
     if (src_format == SDL_PIXELFORMAT_YV12 ||
         src_format == SDL_PIXELFORMAT_IYUV) {
@@ -395,7 +396,7 @@
          Uint32 src_format, const void *src, int src_pitch,
          Uint32 dst_format, void *dst, int dst_pitch)
 {
-	const Uint8 *y = NULL;
+    const Uint8 *y = NULL;
     const Uint8 *u = NULL;
     const Uint8 *v = NULL;
     Uint32 y_stride = 0;
@@ -553,7 +554,7 @@
             Uint32 y_stride, uv_stride, y_skip, uv_skip;
 
             GetYUVPlanes(width, height, dst_format, dst, dst_pitch,
-	                     (const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v,
+                         (const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v,
                          &y_stride, &uv_stride);
             plane_interleaved_uv = (plane_y + height * y_stride);
             y_skip = (y_stride - width);
diff --git a/source/src/video/SDL_yuv_c.h b/source/src/video/SDL_yuv_c.h
index 6fe02b0..192bd2c 100644
--- a/source/src/video/SDL_yuv_c.h
+++ b/source/src/video/SDL_yuv_c.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_yuv_c_h_
+#define SDL_yuv_c_h_
+
 #include "../SDL_internal.h"
 
 
@@ -27,4 +31,6 @@
 extern int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch);
 extern int SDL_ConvertPixels_YUV_to_YUV(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch);
 
+#endif /* SDL_yuv_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/android/SDL_androidmouse.c b/source/src/video/android/SDL_androidmouse.c
index 1c075fb..037b453 100644
--- a/source/src/video/android/SDL_androidmouse.c
+++ b/source/src/video/android/SDL_androidmouse.c
@@ -42,13 +42,162 @@
 #define BUTTON_BACK 8
 #define BUTTON_FORWARD 16
 
+typedef struct
+{
+    int custom_cursor;
+    int system_cursor;
+
+} SDL_AndroidCursorData;
+
 /* Last known Android mouse button state (includes all buttons) */
 static int last_state;
+
+/* Blank cursor */
+static SDL_Cursor *empty_cursor;
+
+static SDL_Cursor *
+Android_WrapCursor(int custom_cursor, int system_cursor)
+{
+    SDL_Cursor *cursor;
+
+    cursor = SDL_calloc(1, sizeof(*cursor));
+    if (cursor) {
+        SDL_AndroidCursorData *data = (SDL_AndroidCursorData*)SDL_calloc(1, sizeof(*data));
+        if (data) {
+            data->custom_cursor = custom_cursor;
+            data->system_cursor = system_cursor;
+            cursor->driverdata = data;
+        } else {
+            SDL_free(cursor);
+            cursor = NULL;
+            SDL_OutOfMemory();
+        }
+    } else {
+        SDL_OutOfMemory();
+    }
+
+    return cursor;
+}
+
+static SDL_Cursor *
+Android_CreateDefaultCursor()
+{
+    return Android_WrapCursor(0, SDL_SYSTEM_CURSOR_ARROW);
+}
+
+static SDL_Cursor *
+Android_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
+{
+    int custom_cursor;
+    SDL_Surface *converted;
+
+    converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0);
+    if (!converted) {
+        return NULL;
+    }
+    custom_cursor = Android_JNI_CreateCustomCursor(converted, hot_x, hot_y);
+    SDL_FreeSurface(converted);
+    if (!custom_cursor) {
+        SDL_Unsupported();
+        return NULL;
+    }
+    return Android_WrapCursor(custom_cursor, 0);
+}
+
+static SDL_Cursor *
+Android_CreateSystemCursor(SDL_SystemCursor id)
+{
+    return Android_WrapCursor(0, id);
+}
+
+static void
+Android_FreeCursor(SDL_Cursor * cursor)
+{
+    SDL_free(cursor->driverdata);
+    SDL_free(cursor);
+}
+
+static SDL_Cursor *
+Android_CreateEmptyCursor()
+{
+    if (!empty_cursor) {
+        SDL_Surface *empty_surface = SDL_CreateRGBSurfaceWithFormat(0, 1, 1, 32, SDL_PIXELFORMAT_ARGB8888);
+        if (empty_surface) {
+            SDL_memset(empty_surface->pixels, 0, empty_surface->h * empty_surface->pitch);
+            empty_cursor = Android_CreateCursor(empty_surface, 0, 0);
+            SDL_FreeSurface(empty_surface);
+        }
+    }
+    return empty_cursor;
+}
+
+static void
+Android_DestroyEmptyCursor()
+{
+    if (empty_cursor) {
+        Android_FreeCursor(empty_cursor);
+        empty_cursor = NULL;
+    }
+}
+
+static int
+Android_ShowCursor(SDL_Cursor * cursor)
+{
+    if (!cursor) {
+        cursor = Android_CreateEmptyCursor();
+    }
+    if (cursor) {
+        SDL_AndroidCursorData *data = (SDL_AndroidCursorData*)cursor->driverdata;
+        if (data->custom_cursor) {
+            if (!Android_JNI_SetCustomCursor(data->custom_cursor)) {
+                return SDL_Unsupported();
+            }
+        } else {
+            if (!Android_JNI_SetSystemCursor(data->system_cursor)) {
+                return SDL_Unsupported();
+            }
+        }
+        return 0;
+    } else {
+        /* SDL error set inside Android_CreateEmptyCursor() */
+        return -1;
+    }
+}
+
+static int
+Android_SetRelativeMouseMode(SDL_bool enabled)
+{
+    if (!Android_JNI_SupportsRelativeMouse()) {
+        return SDL_Unsupported();
+    }
+
+    if (!Android_JNI_SetRelativeMouseEnabled(enabled)) {
+        return SDL_Unsupported();
+    }
+
+    return 0;
+}
 
 void
 Android_InitMouse(void)
 {
+    SDL_Mouse *mouse = SDL_GetMouse();
+
+    mouse->CreateCursor = Android_CreateCursor;
+    mouse->CreateSystemCursor = Android_CreateSystemCursor;
+    mouse->ShowCursor = Android_ShowCursor;
+    mouse->FreeCursor = Android_FreeCursor;
+    mouse->SetRelativeMouseMode = Android_SetRelativeMouseMode;
+
+    SDL_SetDefaultCursor(Android_CreateDefaultCursor());
+
     last_state = 0;
+}
+
+void
+Android_QuitMouse(void)
+{
+    Android_DestroyEmptyCursor();
 }
 
 /* Translate Android mouse button state to SDL mouse button */
@@ -71,7 +220,7 @@
 }
 
 void
-Android_OnMouse(int state, int action, float x, float y)
+Android_OnMouse(int state, int action, float x, float y, SDL_bool relative)
 {
     int changes;
     Uint8 button;
@@ -85,7 +234,7 @@
             changes = state & ~last_state;
             button = TranslateButton(changes);
             last_state = state;
-            SDL_SendMouseMotion(Android_Window, 0, 0, x, y);
+            SDL_SendMouseMotion(Android_Window, 0, relative, x, y);
             SDL_SendMouseButton(Android_Window, 0, SDL_PRESSED, button);
             break;
 
@@ -93,13 +242,13 @@
             changes = last_state & ~state;
             button = TranslateButton(changes);
             last_state = state;
-            SDL_SendMouseMotion(Android_Window, 0, 0, x, y);
+            SDL_SendMouseMotion(Android_Window, 0, relative, x, y);
             SDL_SendMouseButton(Android_Window, 0, SDL_RELEASED, button);
             break;
 
         case ACTION_MOVE:
         case ACTION_HOVER_MOVE:
-            SDL_SendMouseMotion(Android_Window, 0, 0, x, y);
+            SDL_SendMouseMotion(Android_Window, 0, relative, x, y);
             break;
 
         case ACTION_SCROLL:
diff --git a/source/src/video/android/SDL_androidmouse.h b/source/src/video/android/SDL_androidmouse.h
index f201fad..eca9e47 100644
--- a/source/src/video/android/SDL_androidmouse.h
+++ b/source/src/video/android/SDL_androidmouse.h
@@ -25,7 +25,8 @@
 #include "SDL_androidvideo.h"
 
 extern void Android_InitMouse(void);
-extern void Android_OnMouse( int button, int action, float x, float y);
+extern void Android_OnMouse(int button, int action, float x, float y, SDL_bool relative);
+extern void Android_QuitMouse(void);
 
 #endif /* SDL_androidmouse_h_ */
 
diff --git a/source/src/video/android/SDL_androidvideo.c b/source/src/video/android/SDL_androidvideo.c
index 357f5cf..589461a 100644
--- a/source/src/video/android/SDL_androidvideo.c
+++ b/source/src/video/android/SDL_androidvideo.c
@@ -60,8 +60,10 @@
 
 
 /* These are filled in with real values in Android_SetScreenResolution on init (before SDL_main()) */
-int Android_ScreenWidth = 0;
-int Android_ScreenHeight = 0;
+int Android_SurfaceWidth = 0;
+int Android_SurfaceHeight = 0;
+int Android_DeviceWidth = 0;
+int Android_DeviceHeight = 0;
 Uint32 Android_ScreenFormat = SDL_PIXELFORMAT_UNKNOWN;
 static int Android_ScreenRate = 0;
 
@@ -176,8 +178,8 @@
     SDL_DisplayMode mode;
 
     mode.format = Android_ScreenFormat;
-    mode.w = Android_ScreenWidth;
-    mode.h = Android_ScreenHeight;
+    mode.w = Android_DeviceWidth;
+    mode.h = Android_DeviceHeight;
     mode.refresh_rate = Android_ScreenRate;
     mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
@@ -199,6 +201,7 @@
 void
 Android_VideoQuit(_THIS)
 {
+    Android_QuitMouse();
     Android_QuitTouch();
 }
 
@@ -209,12 +212,14 @@
 }
 
 void
-Android_SetScreenResolution(int width, int height, Uint32 format, float rate)
+Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate)
 {
-	SDL_VideoDevice* device;
-	SDL_VideoDisplay *display;
-    Android_ScreenWidth = width;
-    Android_ScreenHeight = height;
+    SDL_VideoDevice* device;
+    SDL_VideoDisplay *display;
+    Android_SurfaceWidth = surfaceWidth;
+    Android_SurfaceHeight = surfaceHeight;
+    Android_DeviceWidth = deviceWidth;
+    Android_DeviceHeight = deviceHeight;
     Android_ScreenFormat = format;
     Android_ScreenRate = rate;
 
@@ -229,8 +234,8 @@
     {
         display = &device->displays[0];
         display->desktop_mode.format = Android_ScreenFormat;
-        display->desktop_mode.w = Android_ScreenWidth;
-        display->desktop_mode.h = Android_ScreenHeight;
+        display->desktop_mode.w = Android_DeviceWidth;
+        display->desktop_mode.h = Android_DeviceHeight;
         display->desktop_mode.refresh_rate  = Android_ScreenRate;
     }
 
@@ -240,12 +245,12 @@
         display = SDL_GetDisplayForWindow(Android_Window);
 
         display->display_modes[0].format = format;
-        display->display_modes[0].w = width;
-        display->display_modes[0].h = height;
+        display->display_modes[0].w = Android_DeviceWidth;
+        display->display_modes[0].h = Android_DeviceHeight;
         display->display_modes[0].refresh_rate = rate;
         display->current_mode = display->display_modes[0];
 
-        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESIZED, width, height);
+        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESIZED, surfaceWidth, surfaceHeight);
     }
 }
 
diff --git a/source/src/video/android/SDL_androidvideo.h b/source/src/video/android/SDL_androidvideo.h
index a62c983..6dce7ed 100644
--- a/source/src/video/android/SDL_androidvideo.h
+++ b/source/src/video/android/SDL_androidvideo.h
@@ -28,7 +28,7 @@
 #include "../SDL_sysvideo.h"
 
 /* Called by the JNI layer when the screen changes size or format */
-extern void Android_SetScreenResolution(int width, int height, Uint32 format, float rate);
+extern void Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate);
 
 /* Private display data */
 
@@ -37,8 +37,10 @@
     SDL_Rect        textRect;
 } SDL_VideoData;
 
-extern int Android_ScreenWidth;
-extern int Android_ScreenHeight;
+extern int Android_SurfaceWidth;
+extern int Android_SurfaceHeight;
+extern int Android_DeviceWidth;
+extern int Android_DeviceHeight;
 extern Uint32 Android_ScreenFormat;
 extern SDL_sem *Android_PauseSem, *Android_ResumeSem;
 extern SDL_Window *Android_Window;
diff --git a/source/src/video/android/SDL_androidwindow.c b/source/src/video/android/SDL_androidwindow.c
index f1cbf58..cf18e67 100644
--- a/source/src/video/android/SDL_androidwindow.c
+++ b/source/src/video/android/SDL_androidwindow.c
@@ -26,6 +26,8 @@
 #include "../SDL_sysvideo.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../events/SDL_mouse_c.h"
+#include "../../events/SDL_windowevents_c.h"
+#include "../../core/android/SDL_android.h"
 
 #include "SDL_androidvideo.h"
 #include "SDL_androidwindow.h"
@@ -49,8 +51,8 @@
     /* Adjust the window data to match the screen */
     window->x = 0;
     window->y = 0;
-    window->w = Android_ScreenWidth;
-    window->h = Android_ScreenHeight;
+    window->w = Android_SurfaceWidth;
+    window->h = Android_SurfaceHeight;
 
     window->flags &= ~SDL_WINDOW_RESIZABLE;     /* window is NEVER resizeable */
     window->flags &= ~SDL_WINDOW_HIDDEN;
@@ -100,7 +102,36 @@
 void
 Android_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
 {
-    Android_JNI_SetWindowStyle(fullscreen);
+    /* If the window is being destroyed don't change visible state */
+    if (!window->is_destroying) {
+        Android_JNI_SetWindowStyle(fullscreen);
+    }
+
+    /* Ensure our size matches reality after we've executed the window style change.
+     *
+     * It is possible that we've set width and height to the full-size display, but on
+     * Samsung DeX or Chromebooks or other windowed Android environemtns, our window may 
+     * still not be the full display size.
+     */
+    if (!SDL_IsDeXMode() && !SDL_IsChromebook()) {
+        return;
+    }
+
+    SDL_WindowData * data = (SDL_WindowData *)window->driverdata;
+
+    if (!data || !data->native_window) {
+        return;
+    }
+
+    int old_w = window->w;
+    int old_h = window->h;
+
+    int new_w = ANativeWindow_getWidth(data->native_window);
+    int new_h = ANativeWindow_getHeight(data->native_window);
+
+    if (old_w != new_w || old_h != new_h) {
+        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, new_w, new_h);
+    }
 }
 
 void
diff --git a/source/src/video/cocoa/SDL_cocoaevents.m b/source/src/video/cocoa/SDL_cocoaevents.m
index 38f4ba6..76d235e 100644
--- a/source/src/video/cocoa/SDL_cocoaevents.m
+++ b/source/src/video/cocoa/SDL_cocoaevents.m
@@ -390,8 +390,8 @@
 
         if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) {
             [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
-		}
-		
+        }
+
         if ([NSApp mainMenu] == nil) {
             CreateApplicationMenus();
         }
diff --git a/source/src/video/cocoa/SDL_cocoametalview.h b/source/src/video/cocoa/SDL_cocoametalview.h
index c0a582f..185d45d 100644
--- a/source/src/video/cocoa/SDL_cocoametalview.h
+++ b/source/src/video/cocoa/SDL_cocoametalview.h
@@ -39,16 +39,16 @@
 
 #define METALVIEW_TAG 255
 
-@interface SDL_cocoametalview : NSView {
-    NSInteger _tag;
-}
+@interface SDL_cocoametalview : NSView
 
 - (instancetype)initWithFrame:(NSRect)frame
-                        scale:(CGFloat)scale;
+                      highDPI:(BOOL)highDPI;
 
 /* Override superclass tag so this class can set it. */
 @property (assign, readonly) NSInteger tag;
 
+@property (nonatomic) BOOL highDPI;
+
 @end
 
 SDL_cocoametalview* Cocoa_Mtl_AddMetalView(SDL_Window* window);
diff --git a/source/src/video/cocoa/SDL_cocoametalview.m b/source/src/video/cocoa/SDL_cocoametalview.m
index e9c08a0..9447fb8 100644
--- a/source/src/video/cocoa/SDL_cocoametalview.m
+++ b/source/src/video/cocoa/SDL_cocoametalview.m
@@ -33,13 +33,10 @@
 
 @implementation SDL_cocoametalview
 
-/* The synthesized getter should be called by super's viewWithTag. */
-@synthesize tag = _tag;
-
 /* Return a Metal-compatible layer. */
 + (Class)layerClass
 {
-	return NSClassFromString(@"CAMetalLayer");
+    return NSClassFromString(@"CAMetalLayer");
 }
 
 /* Indicate the view wants to draw using a backing layer instead of drawRect. */
@@ -57,28 +54,48 @@
 }
 
 - (instancetype)initWithFrame:(NSRect)frame
-                        scale:(CGFloat)scale
+                      highDPI:(BOOL)highDPI
 {
-	if ((self = [super initWithFrame:frame])) {
-        _tag = METALVIEW_TAG;
+    if ((self = [super initWithFrame:frame])) {
+        self.highDPI = highDPI;
         self.wantsLayer = YES;
 
         /* Allow resize. */
         self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
 
-        /* Set the desired scale. The default drawableSize of a CAMetalLayer
-         * is its bounds x its scale so nothing further needs to be done.
-         */
-        self.layer.contentsScale = scale;
-	}
+        [self updateDrawableSize];
+    }
   
-	return self;
+    return self;
+}
+
+- (NSInteger)tag
+{
+    return METALVIEW_TAG;
+}
+
+- (void)updateDrawableSize
+{
+    CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
+    CGSize size = self.bounds.size;
+    CGSize backingSize = size;
+
+    if (self.highDPI) {
+        /* Note: NSHighResolutionCapable must be set to true in the app's
+         * Info.plist in order for the backing size to be high res.
+         */
+        backingSize = [self convertSizeToBacking:size];
+    }
+
+    metalLayer.contentsScale = backingSize.height / size.height;
+    metalLayer.drawableSize = backingSize;
 }
 
 /* Set the size of the metal drawables when the view is resized. */
 - (void)resizeWithOldSuperviewSize:(NSSize)oldSize
 {
     [super resizeWithOldSuperviewSize:oldSize];
+    [self updateDrawableSize];
 }
 
 @end
@@ -88,24 +105,10 @@
 {
     SDL_WindowData* data = (__bridge SDL_WindowData *)window->driverdata;
     NSView *view = data->nswindow.contentView;
-    CGFloat scale = 1.0;
+    BOOL highDPI = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
+    SDL_cocoametalview *metalview;
 
-    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
-        /* Set the scale to the natural scale factor of the screen - then
-         * the backing dimensions of the Metal view will match the pixel
-         * dimensions of the screen rather than the dimensions in points
-         * yielding high resolution on retine displays.
-         *
-         * N.B. In order for backingScaleFactor to be > 1,
-         * NSHighResolutionCapable must be set to true in the app's Info.plist.
-         */
-        NSWindow* nswindow = data->nswindow;
-        if ([nswindow.screen respondsToSelector:@selector(backingScaleFactor)])
-            scale = data->nswindow.screen.backingScaleFactor;
-    }
-        
-    SDL_cocoametalview *metalview
-        = [[SDL_cocoametalview alloc] initWithFrame:view.frame scale:scale];
+    metalview = [[SDL_cocoametalview alloc] initWithFrame:view.frame highDPI:highDPI];
     [view addSubview:metalview];
     return metalview;
 }
diff --git a/source/src/video/cocoa/SDL_cocoamouse.m b/source/src/video/cocoa/SDL_cocoamouse.m
index 029a318..c9db253 100644
--- a/source/src/video/cocoa/SDL_cocoamouse.m
+++ b/source/src/video/cocoa/SDL_cocoamouse.m
@@ -432,6 +432,16 @@
         }
     }
 
+    if (x > 0) {
+        x = SDL_ceil(x);
+    } else if (x < 0) {
+        x = SDL_floor(x);
+    }
+    if (y > 0) {
+        y = SDL_ceil(y);
+    } else if (y < 0) {
+        y = SDL_floor(y);
+    }
     SDL_SendMouseWheel(window, mouse->mouseID, x, y, direction);
 }
 
diff --git a/source/src/video/cocoa/SDL_cocoamousetap.m b/source/src/video/cocoa/SDL_cocoamousetap.m
index 3c4fcf2..aa4f152 100644
--- a/source/src/video/cocoa/SDL_cocoamousetap.m
+++ b/source/src/video/cocoa/SDL_cocoamousetap.m
@@ -211,7 +211,7 @@
             tapdata->thread = SDL_CreateThreadInternal(&Cocoa_MouseTapThread, "Event Tap Loop", 512 * 1024, tapdata);
             if (tapdata->thread) {
                 /* Success - early out. Ownership transferred to thread. */
-            	return;
+                return;
             }
             CFRelease(tapdata->tap);
         }
@@ -237,6 +237,13 @@
     SDL_MouseEventTapData *tapdata = (SDL_MouseEventTapData*)driverdata->tapdata;
     int status;
 
+    if (tapdata == NULL) {
+        /* event tap was already cleaned up (possibly due to CGEventTapCreate
+         * returning null.)
+         */
+        return;
+    }
+
     /* Ensure that the runloop has been started first.
      * TODO: Move this to InitMouseEventTap, check for error conditions that can
      * happen in Cocoa_MouseTapThread, and fall back to the non-EventTap way of
diff --git a/source/src/video/cocoa/SDL_cocoaopengl.m b/source/src/video/cocoa/SDL_cocoaopengl.m
index 5f18a2e..9539c17 100644
--- a/source/src/video/cocoa/SDL_cocoaopengl.m
+++ b/source/src/video/cocoa/SDL_cocoaopengl.m
@@ -347,10 +347,12 @@
     NSView *contentView = [windata->nswindow contentView];
     NSRect viewport = [contentView bounds];
 
-    /* This gives us the correct viewport for a Retina-enabled view, only
-     * supported on 10.7+. */
-    if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
-        viewport = [contentView convertRectToBacking:viewport];
+    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
+        /* This gives us the correct viewport for a Retina-enabled view, only
+         * supported on 10.7+. */
+        if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
+            viewport = [contentView convertRectToBacking:viewport];
+        }
     }
 
     if (w) {
@@ -408,8 +410,14 @@
 { @autoreleasepool
 {
     SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext();
+    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+
+    /* on 10.14 ("Mojave") and later, this deadlocks if two contexts in two
+       threads try to swap at the same time, so put a mutex around it. */
+    SDL_LockMutex(videodata->swaplock);
     [nscontext flushBuffer];
     [nscontext updateIfNeeded];
+    SDL_UnlockMutex(videodata->swaplock);
     return 0;
 }}
 
diff --git a/source/src/video/cocoa/SDL_cocoavideo.h b/source/src/video/cocoa/SDL_cocoavideo.h
index 05bbd34..b1c26fa 100644
--- a/source/src/video/cocoa/SDL_cocoavideo.h
+++ b/source/src/video/cocoa/SDL_cocoavideo.h
@@ -107,7 +107,7 @@
     Uint32 screensaver_activity;
     BOOL screensaver_use_iopm;
     IOPMAssertionID screensaver_assertion;
-
+    SDL_mutex *swaplock;
 } SDL_VideoData;
 
 /* Utility functions */
diff --git a/source/src/video/cocoa/SDL_cocoavideo.m b/source/src/video/cocoa/SDL_cocoavideo.m
index 545dc1e..20bdfa7 100644
--- a/source/src/video/cocoa/SDL_cocoavideo.m
+++ b/source/src/video/cocoa/SDL_cocoavideo.m
@@ -105,6 +105,7 @@
     device->DestroyWindow = Cocoa_DestroyWindow;
     device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
     device->SetWindowHitTest = Cocoa_SetWindowHitTest;
+    device->AcceptDragAndDrop = Cocoa_AcceptDragAndDrop;
 
     device->shape_driver.CreateShaper = Cocoa_CreateShaper;
     device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
@@ -174,15 +175,23 @@
     /* The IOPM assertion API can disable the screensaver as of 10.7. */
     data->screensaver_use_iopm = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6;
 
+    data->swaplock = SDL_CreateMutex();
+    if (!data->swaplock) {
+        return -1;
+    }
+
     return 0;
 }
 
 void
 Cocoa_VideoQuit(_THIS)
 {
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     Cocoa_QuitModes(_this);
     Cocoa_QuitKeyboard(_this);
     Cocoa_QuitMouse(_this);
+    SDL_DestroyMutex(data->swaplock);
+    data->swaplock = NULL;
 }
 
 /* This function assumes that it's called from within an autorelease pool */
diff --git a/source/src/video/cocoa/SDL_cocoavulkan.m b/source/src/video/cocoa/SDL_cocoavulkan.m
index 2cf55bb..0e53d21 100644
--- a/source/src/video/cocoa/SDL_cocoavulkan.m
+++ b/source/src/video/cocoa/SDL_cocoavulkan.m
@@ -58,8 +58,7 @@
     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
 
     if (_this->vulkan_config.loader_handle) {
-        SDL_SetError("Vulkan/MoltenVK already loaded");
-        return -1;
+        return SDL_SetError("Vulkan/MoltenVK already loaded");
     }
 
     /* Load the Vulkan loader library */
@@ -80,6 +79,7 @@
         _this->vulkan_config.loader_handle = DEFAULT_HANDLE;
     } else {
         const char** paths;
+        const char *foundPath = NULL;
         int numPaths;
         int i;
 
@@ -92,18 +92,17 @@
             paths = defaultPaths;
             numPaths = SDL_arraysize(defaultPaths);
         }
-        
-        for (i=0; i < numPaths; i++) {
-            _this->vulkan_config.loader_handle = SDL_LoadObject(paths[i]);
-            if (_this->vulkan_config.loader_handle)
-                break;
-            else
-                continue;
-        }
-        if (i == numPaths)
-            return -1;
 
-        SDL_strlcpy(_this->vulkan_config.loader_path, paths[i],
+        for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) {
+            foundPath = paths[i];
+            _this->vulkan_config.loader_handle = SDL_LoadObject(foundPath);
+        }
+
+        if (_this->vulkan_config.loader_handle == NULL) {
+            return SDL_SetError("Failed to load Vulkan/MoltenVK library");
+        }
+
+        SDL_strlcpy(_this->vulkan_config.loader_path, foundPath,
                     SDL_arraysize(_this->vulkan_config.loader_path));
         vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
             _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
diff --git a/source/src/video/cocoa/SDL_cocoawindow.h b/source/src/video/cocoa/SDL_cocoawindow.h
index df6f173..2311e3d 100644
--- a/source/src/video/cocoa/SDL_cocoawindow.h
+++ b/source/src/video/cocoa/SDL_cocoawindow.h
@@ -148,6 +148,7 @@
 extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
 extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
 extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
+extern void Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
 
 #endif /* SDL_cocoawindow_h_ */
 
diff --git a/source/src/video/cocoa/SDL_cocoawindow.m b/source/src/video/cocoa/SDL_cocoawindow.m
index b1a5b46..a8e95cc 100644
--- a/source/src/video/cocoa/SDL_cocoawindow.m
+++ b/source/src/video/cocoa/SDL_cocoawindow.m
@@ -241,7 +241,7 @@
 static int
 GetHintCtrlClickEmulateRightClick()
 {
-	return SDL_GetHintBoolean(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, SDL_FALSE);
+    return SDL_GetHintBoolean(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, SDL_FALSE);
 }
 
 static NSUInteger
@@ -908,7 +908,7 @@
     switch ([theEvent buttonNumber]) {
     case 0:
         if (([theEvent modifierFlags] & NSEventModifierFlagControl) &&
-		    GetHintCtrlClickEmulateRightClick()) {
+            GetHintCtrlClickEmulateRightClick()) {
             wasCtrlLeft = YES;
             button = SDL_BUTTON_RIGHT;
         } else {
@@ -1143,14 +1143,18 @@
 - (BOOL)mouseDownCanMoveWindow;
 - (void)drawRect:(NSRect)dirtyRect;
 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent;
+- (BOOL)wantsUpdateLayer;
+- (void)updateLayer;
 @end
 
 @implementation SDLView
+
 - (void)setSDLWindow:(SDL_Window*)window
 {
     _sdlWindow = window;
 }
 
+/* this is used on older macOS revisions. 10.8 and later use updateLayer. */
 - (void)drawRect:(NSRect)dirtyRect
 {
     /* Force the graphics context to clear to black so we don't get a flash of
@@ -1158,6 +1162,21 @@
        only gets called for window creation and other extraordinary events. */
     [[NSColor blackColor] setFill];
     NSRectFill(dirtyRect);
+    SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
+}
+
+-(BOOL) wantsUpdateLayer
+{
+    return YES;
+}
+
+-(void) updateLayer
+{
+    /* Force the graphics context to clear to black so we don't get a flash of
+       white until the app is ready to draw. In practice on modern macOS, this
+       only gets called for window creation and other extraordinary events. */
+    self.layer.backgroundColor = NSColor.blackColor.CGColor;
+    ScheduleContextUpdates((SDL_WindowData *) _sdlWindow->driverdata);
     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
 }
 
@@ -1343,6 +1362,7 @@
             [contentView setWantsBestResolutionOpenGLSurface:YES];
         }
     }
+
 #if SDL_VIDEO_OPENGL_ES2
 #if SDL_VIDEO_OPENGL_EGL
     if ((window->flags & SDL_WINDOW_OPENGL) &&
@@ -1353,9 +1373,6 @@
 #endif /* SDL_VIDEO_OPENGL_ES2 */
     [nswindow setContentView:contentView];
     [contentView release];
-
-    /* Allow files and folders to be dragged onto the window by users */
-    [nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
 
     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
         [nswindow release];
@@ -1864,6 +1881,17 @@
     return 0;  /* just succeed, the real work is done elsewhere. */
 }
 
+void
+Cocoa_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    if (accept) {
+        [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
+    } else {
+        [data->nswindow unregisterDraggedTypes];
+    }
+}
+
 int
 Cocoa_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
 {
diff --git a/source/src/video/directfb/SDL_DirectFB_video.c b/source/src/video/directfb/SDL_DirectFB_video.c
index 45fa81d..8740ce1 100644
--- a/source/src/video/directfb/SDL_DirectFB_video.c
+++ b/source/src/video/directfb/SDL_DirectFB_video.c
@@ -324,6 +324,7 @@
     { DSPF_YUY2, SDL_PIXELFORMAT_YUY2 },                /* 16 bit YUV (4 byte/ 2 pixel, macropixel contains CbYCrY [31:0]) */
     { DSPF_UYVY, SDL_PIXELFORMAT_UYVY },                /* 16 bit YUV (4 byte/ 2 pixel, macropixel contains YCbYCr [31:0]) */
     { DSPF_RGB555, SDL_PIXELFORMAT_RGB555 },            /* 16 bit RGB (2 byte, nothing @15, red 5@10, green 5@5, blue 5@0) */
+    { DSPF_ABGR, SDL_PIXELFORMAT_ABGR8888 },            /* 32 bit ABGR (4  byte, alpha 8@24, blue 8@16, green 8@8, red 8@0) */
 #if (ENABLE_LUT8)
     { DSPF_LUT8, SDL_PIXELFORMAT_INDEX8 },              /* 8 bit LUT (8 bit color and alpha lookup from palette) */
 #endif
@@ -370,7 +371,6 @@
     { DSPF_UNKNOWN, SDL_PIXELFORMAT_BGR24 },
     { DSPF_UNKNOWN, SDL_PIXELFORMAT_BGR888 },
     { DSPF_UNKNOWN, SDL_PIXELFORMAT_RGBA8888 },
-    { DSPF_UNKNOWN, SDL_PIXELFORMAT_ABGR8888 },
     { DSPF_UNKNOWN, SDL_PIXELFORMAT_BGRA8888 },
     { DSPF_UNKNOWN, SDL_PIXELFORMAT_ARGB2101010 },
     { DSPF_UNKNOWN, SDL_PIXELFORMAT_ABGR4444 },
diff --git a/source/src/video/dummy/SDL_nullevents_c.h b/source/src/video/dummy/SDL_nullevents_c.h
index a5636be..454d394 100644
--- a/source/src/video/dummy/SDL_nullevents_c.h
+++ b/source/src/video/dummy/SDL_nullevents_c.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_nullevents_c_h_
+#define SDL_nullevents_c_h_
+
 #include "../../SDL_internal.h"
 
 #include "SDL_nullvideo.h"
 
 extern void DUMMY_PumpEvents(_THIS);
 
+#endif /* SDL_nullevents_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/dummy/SDL_nullframebuffer_c.h b/source/src/video/dummy/SDL_nullframebuffer_c.h
index 5d6b7ae..b7d0c63 100644
--- a/source/src/video/dummy/SDL_nullframebuffer_c.h
+++ b/source/src/video/dummy/SDL_nullframebuffer_c.h
@@ -18,10 +18,16 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_nullframebuffer_c_h_
+#define SDL_nullframebuffer_c_h_
+
 #include "../../SDL_internal.h"
 
 extern int SDL_DUMMY_CreateWindowFramebuffer(_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
 extern int SDL_DUMMY_UpdateWindowFramebuffer(_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
 extern void SDL_DUMMY_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
 
+#endif /* SDL_nullframebuffer_c_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/emscripten/SDL_emscriptenmouse.c b/source/src/video/emscripten/SDL_emscriptenmouse.c
index 490f5b0..e120980 100644
--- a/source/src/video/emscripten/SDL_emscriptenmouse.c
+++ b/source/src/video/emscripten/SDL_emscriptenmouse.c
@@ -165,6 +165,7 @@
             cursor_name = "ns-resize";
             break;
         case SDL_SYSTEM_CURSOR_SIZEALL:
+            cursor_name = "move";
             break;
         case SDL_SYSTEM_CURSOR_NO:
             cursor_name = "not-allowed";
diff --git a/source/src/video/emscripten/SDL_emscriptenopengles.c b/source/src/video/emscripten/SDL_emscriptenopengles.c
index a6609ed..7d8c005 100644
--- a/source/src/video/emscripten/SDL_emscriptenopengles.c
+++ b/source/src/video/emscripten/SDL_emscriptenopengles.c
@@ -60,7 +60,9 @@
     LOAD_FUNC(eglWaitNative);
     LOAD_FUNC(eglWaitGL);
     LOAD_FUNC(eglBindAPI);
-    
+    LOAD_FUNC(eglQueryString);
+    LOAD_FUNC(eglGetError);
+
     _this->egl_data->egl_display = _this->egl_data->eglGetDisplay(EGL_DEFAULT_DISPLAY);
     if (!_this->egl_data->egl_display) {
         return SDL_SetError("Could not get EGL display");
diff --git a/source/src/video/haiku/SDL_BWin.h b/source/src/video/haiku/SDL_BWin.h
index 3e61888..b22f74b 100644
--- a/source/src/video/haiku/SDL_BWin.h
+++ b/source/src/video/haiku/SDL_BWin.h
@@ -88,7 +88,7 @@
         _clips = NULL;
 
 #ifdef DRAWTHREAD
-        _draw_thread_id = spawn_thread(BE_DrawThread, "drawing_thread",
+        _draw_thread_id = spawn_thread(HAIKU_DrawThread, "drawing_thread",
                             B_NORMAL_PRIORITY, (void*) this);
         resume_thread(_draw_thread_id);
 #endif
@@ -538,7 +538,7 @@
         msg.AddInt32("key-state", keyState);
         msg.AddInt32("key-scancode", keyCode);
         if (keyUtf8 != NULL) {
-        	msg.AddData("key-utf8", B_INT8_TYPE, (const void*)keyUtf8, len);
+            msg.AddData("key-utf8", B_INT8_TYPE, (const void*)keyUtf8, len);
         }
         be_app->PostMessage(&msg);
     }
diff --git a/source/src/video/haiku/SDL_bclipboard.cc b/source/src/video/haiku/SDL_bclipboard.cc
index e7d01b9..3138603 100644
--- a/source/src/video/haiku/SDL_bclipboard.cc
+++ b/source/src/video/haiku/SDL_bclipboard.cc
@@ -35,55 +35,55 @@
 extern "C" {
 #endif
 
-int BE_SetClipboardText(_THIS, const char *text) {
-	BMessage *clip = NULL;
-	if(be_clipboard->Lock()) {
-		be_clipboard->Clear();
-		if((clip = be_clipboard->Data())) {
-			/* Presumably the string of characters is ascii-format */
-			ssize_t asciiLength = 0;
-			for(; text[asciiLength] != 0; ++asciiLength) {}
-			clip->AddData("text/plain", B_MIME_TYPE, text, asciiLength);
-			be_clipboard->Commit();
-		}
-		be_clipboard->Unlock();
-	}
-	return 0;
+int HAIKU_SetClipboardText(_THIS, const char *text) {
+    BMessage *clip = NULL;
+    if(be_clipboard->Lock()) {
+        be_clipboard->Clear();
+        if((clip = be_clipboard->Data())) {
+            /* Presumably the string of characters is ascii-format */
+            ssize_t asciiLength = 0;
+            for(; text[asciiLength] != 0; ++asciiLength) {}
+            clip->AddData("text/plain", B_MIME_TYPE, text, asciiLength);
+            be_clipboard->Commit();
+        }
+        be_clipboard->Unlock();
+    }
+    return 0;
 }
 
-char *BE_GetClipboardText(_THIS) {
-	BMessage *clip = NULL;
-	const char *text = NULL;	
-	ssize_t length;
-	char *result;
-	if(be_clipboard->Lock()) {
-		if((clip = be_clipboard->Data())) {
-			/* Presumably the string of characters is ascii-format */
-			clip->FindData("text/plain", B_MIME_TYPE, (const void**)&text,
-				&length);
-		}
-		be_clipboard->Unlock();
-	} 
-	
-	if (!text) {
-		result = SDL_strdup("");
-	} else {
-		/* Copy the data and pass on to SDL */
-		result = (char *)SDL_malloc((length + 1) * sizeof(char));
-		SDL_strlcpy(result, text, length + 1);
-	}
-	
-	return result;
+char *HAIKU_GetClipboardText(_THIS) {
+    BMessage *clip = NULL;
+    const char *text = NULL;    
+    ssize_t length;
+    char *result;
+    if(be_clipboard->Lock()) {
+        if((clip = be_clipboard->Data())) {
+            /* Presumably the string of characters is ascii-format */
+            clip->FindData("text/plain", B_MIME_TYPE, (const void**)&text,
+                &length);
+        }
+        be_clipboard->Unlock();
+    } 
+    
+    if (!text) {
+        result = SDL_strdup("");
+    } else {
+        /* Copy the data and pass on to SDL */
+        result = (char *)SDL_malloc((length + 1) * sizeof(char));
+        SDL_strlcpy(result, text, length + 1);
+    }
+    
+    return result;
 }
 
-SDL_bool BE_HasClipboardText(_THIS) {
-	SDL_bool result = SDL_FALSE;
-	char *text = BE_GetClipboardText(_this);
-	if (text) {
-		result = text[0] != '\0' ? SDL_TRUE : SDL_FALSE;
-		SDL_free(text);
-	} 
-	return result;
+SDL_bool HAIKU_HasClipboardText(_THIS) {
+    SDL_bool result = SDL_FALSE;
+    char *text = HAIKU_GetClipboardText(_this);
+    if (text) {
+        result = text[0] != '\0' ? SDL_TRUE : SDL_FALSE;
+        SDL_free(text);
+    } 
+    return result;
 }
 
 #ifdef __cplusplus
diff --git a/source/src/video/haiku/SDL_bclipboard.h b/source/src/video/haiku/SDL_bclipboard.h
index 2f7a1c2..de69ed3 100644
--- a/source/src/video/haiku/SDL_bclipboard.h
+++ b/source/src/video/haiku/SDL_bclipboard.h
@@ -24,9 +24,9 @@
 #ifndef SDL_BCLIPBOARD_H
 #define SDL_BCLIPBOARD_H
 
-extern int BE_SetClipboardText(_THIS, const char *text);
-extern char *BE_GetClipboardText(_THIS);
-extern SDL_bool BE_HasClipboardText(_THIS);
+extern int HAIKU_SetClipboardText(_THIS, const char *text);
+extern char *HAIKU_GetClipboardText(_THIS);
+extern SDL_bool HAIKU_HasClipboardText(_THIS);
 
 #endif
 
diff --git a/source/src/video/haiku/SDL_bevents.cc b/source/src/video/haiku/SDL_bevents.cc
index c716731..c918ab2 100644
--- a/source/src/video/haiku/SDL_bevents.cc
+++ b/source/src/video/haiku/SDL_bevents.cc
@@ -28,8 +28,8 @@
 extern "C" {
 #endif
 
-void BE_PumpEvents(_THIS) {
-	/* Since the event thread is its own thread, this isn't really necessary */
+void HAIKU_PumpEvents(_THIS) {
+    /* Since the event thread is its own thread, this isn't really necessary */
 }
 
 #ifdef __cplusplus
diff --git a/source/src/video/haiku/SDL_bevents.h b/source/src/video/haiku/SDL_bevents.h
index 3c090c8..5c34fcf 100644
--- a/source/src/video/haiku/SDL_bevents.h
+++ b/source/src/video/haiku/SDL_bevents.h
@@ -28,7 +28,7 @@
 extern "C" {
 #endif
 
-extern void BE_PumpEvents(_THIS);
+extern void HAIKU_PumpEvents(_THIS);
 
 #ifdef __cplusplus
 }
diff --git a/source/src/video/haiku/SDL_bframebuffer.cc b/source/src/video/haiku/SDL_bframebuffer.cc
index f53c500..9675706 100644
--- a/source/src/video/haiku/SDL_bframebuffer.cc
+++ b/source/src/video/haiku/SDL_bframebuffer.cc
@@ -36,162 +36,162 @@
 #endif
 
 #ifndef DRAWTHREAD
-static int32 BE_UpdateOnce(SDL_Window *window);
+static int32 HAIKU_UpdateOnce(SDL_Window *window);
 #endif
 
 static SDL_INLINE SDL_BWin *_ToBeWin(SDL_Window *window) {
-	return ((SDL_BWin*)(window->driverdata));
+    return ((SDL_BWin*)(window->driverdata));
 }
 
 static SDL_INLINE SDL_BApp *_GetBeApp() {
-	return ((SDL_BApp*)be_app);
+    return ((SDL_BApp*)be_app);
 }
 
-int BE_CreateWindowFramebuffer(_THIS, SDL_Window * window,
+int HAIKU_CreateWindowFramebuffer(_THIS, SDL_Window * window,
                                        Uint32 * format,
                                        void ** pixels, int *pitch) {
-	SDL_BWin *bwin = _ToBeWin(window);
-	BScreen bscreen;
-	if(!bscreen.IsValid()) {
-		return -1;
-	}
+    SDL_BWin *bwin = _ToBeWin(window);
+    BScreen bscreen;
+    if(!bscreen.IsValid()) {
+        return -1;
+    }
 
-	while(!bwin->Connected()) { snooze(100); }
-	
-	/* Make sure we have exclusive access to frame buffer data */
-	bwin->LockBuffer();
+    while(!bwin->Connected()) { snooze(100); }
+    
+    /* Make sure we have exclusive access to frame buffer data */
+    bwin->LockBuffer();
 
-	/* format */
-	display_mode bmode;
-	bscreen.GetMode(&bmode);
-	int32 bpp = BE_ColorSpaceToBitsPerPixel(bmode.space);
-	*format = BE_BPPToSDLPxFormat(bpp);
+    /* format */
+    display_mode bmode;
+    bscreen.GetMode(&bmode);
+    int32 bpp = HAIKU_ColorSpaceToBitsPerPixel(bmode.space);
+    *format = HAIKU_BPPToSDLPxFormat(bpp);
 
-	/* Create the new bitmap object */
-	BBitmap *bitmap = bwin->GetBitmap();
+    /* Create the new bitmap object */
+    BBitmap *bitmap = bwin->GetBitmap();
 
-	if(bitmap) {
-		delete bitmap;
-	}
-	bitmap = new BBitmap(bwin->Bounds(), (color_space)bmode.space,
-			false,	/* Views not accepted */
-			true);	/* Contiguous memory required */
-			
-	if(bitmap->InitCheck() != B_OK) {
-		delete bitmap;
-		return SDL_SetError("Could not initialize back buffer!");
-	}
+    if(bitmap) {
+        delete bitmap;
+    }
+    bitmap = new BBitmap(bwin->Bounds(), (color_space)bmode.space,
+            false,    /* Views not accepted */
+            true);    /* Contiguous memory required */
+            
+    if(bitmap->InitCheck() != B_OK) {
+        delete bitmap;
+        return SDL_SetError("Could not initialize back buffer!");
+    }
 
 
-	bwin->SetBitmap(bitmap);
-	
-	/* Set the pixel pointer */
-	*pixels = bitmap->Bits();
+    bwin->SetBitmap(bitmap);
+    
+    /* Set the pixel pointer */
+    *pixels = bitmap->Bits();
 
-	/* pitch = width of window, in bytes */
-	*pitch = bitmap->BytesPerRow();
+    /* pitch = width of window, in bytes */
+    *pitch = bitmap->BytesPerRow();
 
-	bwin->SetBufferExists(true);
-	bwin->SetTrashBuffer(false);
-	bwin->UnlockBuffer();
-	return 0;
+    bwin->SetBufferExists(true);
+    bwin->SetTrashBuffer(false);
+    bwin->UnlockBuffer();
+    return 0;
 }
 
 
 
-int BE_UpdateWindowFramebuffer(_THIS, SDL_Window * window,
+int HAIKU_UpdateWindowFramebuffer(_THIS, SDL_Window * window,
                                       const SDL_Rect * rects, int numrects) {
-	if(!window)
-		return 0;
+    if(!window)
+        return 0;
 
-	SDL_BWin *bwin = _ToBeWin(window);
+    SDL_BWin *bwin = _ToBeWin(window);
 
-#ifdef DRAWTHREAD	
-	bwin->LockBuffer();
-	bwin->SetBufferDirty(true);
-	bwin->UnlockBuffer();
+#ifdef DRAWTHREAD    
+    bwin->LockBuffer();
+    bwin->SetBufferDirty(true);
+    bwin->UnlockBuffer();
 #else
-	bwin->SetBufferDirty(true);
-	BE_UpdateOnce(window);
+    bwin->SetBufferDirty(true);
+    HAIKU_UpdateOnce(window);
 #endif
 
-	return 0;
+    return 0;
 }
 
-int32 BE_DrawThread(void *data) {
-	SDL_BWin *bwin = (SDL_BWin*)data;
-	
-	BScreen bscreen;
-	if(!bscreen.IsValid()) {
-		return -1;
-	}
+int32 HAIKU_DrawThread(void *data) {
+    SDL_BWin *bwin = (SDL_BWin*)data;
+    
+    BScreen bscreen;
+    if(!bscreen.IsValid()) {
+        return -1;
+    }
 
-	while(bwin->ConnectionEnabled()) {
-		if( bwin->Connected() && bwin->BufferExists() && bwin->BufferIsDirty() ) {
-			bwin->LockBuffer();
-			BBitmap *bitmap = NULL;
-			bitmap = bwin->GetBitmap();
-			int32 windowPitch = bitmap->BytesPerRow();
-			int32 bufferPitch = bwin->GetRowBytes();
-			uint8 *windowpx;
-			uint8 *bufferpx;
+    while(bwin->ConnectionEnabled()) {
+        if( bwin->Connected() && bwin->BufferExists() && bwin->BufferIsDirty() ) {
+            bwin->LockBuffer();
+            BBitmap *bitmap = NULL;
+            bitmap = bwin->GetBitmap();
+            int32 windowPitch = bitmap->BytesPerRow();
+            int32 bufferPitch = bwin->GetRowBytes();
+            uint8 *windowpx;
+            uint8 *bufferpx;
 
-			int32 BPP = bwin->GetBytesPerPx();
-			int32 windowSub = bwin->GetFbX() * BPP +
-						  bwin->GetFbY() * windowPitch;
-			clipping_rect *clips = bwin->GetClips();
-			int32 numClips = bwin->GetNumClips();
-			int i, y;
+            int32 BPP = bwin->GetBytesPerPx();
+            int32 windowSub = bwin->GetFbX() * BPP +
+                          bwin->GetFbY() * windowPitch;
+            clipping_rect *clips = bwin->GetClips();
+            int32 numClips = bwin->GetNumClips();
+            int i, y;
 
-			/* Blit each clipping rectangle */
-			bscreen.WaitForRetrace();
-			for(i = 0; i < numClips; ++i) {
-				/* Get addresses of the start of each clipping rectangle */
-				int32 width = clips[i].right - clips[i].left + 1;
-				int32 height = clips[i].bottom - clips[i].top + 1;
-				bufferpx = bwin->GetBufferPx() + 
-					clips[i].top * bufferPitch + clips[i].left * BPP;
-				windowpx = (uint8*)bitmap->Bits() + 
-					clips[i].top * windowPitch + clips[i].left * BPP -
-					windowSub;
+            /* Blit each clipping rectangle */
+            bscreen.WaitForRetrace();
+            for(i = 0; i < numClips; ++i) {
+                /* Get addresses of the start of each clipping rectangle */
+                int32 width = clips[i].right - clips[i].left + 1;
+                int32 height = clips[i].bottom - clips[i].top + 1;
+                bufferpx = bwin->GetBufferPx() + 
+                    clips[i].top * bufferPitch + clips[i].left * BPP;
+                windowpx = (uint8*)bitmap->Bits() + 
+                    clips[i].top * windowPitch + clips[i].left * BPP -
+                    windowSub;
 
-				/* Copy each row of pixels from the window buffer into the frame
-				   buffer */
-				for(y = 0; y < height; ++y)
-				{
+                /* Copy each row of pixels from the window buffer into the frame
+                   buffer */
+                for(y = 0; y < height; ++y)
+                {
 
-					if(bwin->CanTrashWindowBuffer()) {
-						goto escape;	/* Break out before the buffer is killed */
-					}
+                    if(bwin->CanTrashWindowBuffer()) {
+                        goto escape;    /* Break out before the buffer is killed */
+                    }
 
-					memcpy(bufferpx, windowpx, width * BPP);
-					bufferpx += bufferPitch;
-					windowpx += windowPitch;
-				}
-			}
+                    memcpy(bufferpx, windowpx, width * BPP);
+                    bufferpx += bufferPitch;
+                    windowpx += windowPitch;
+                }
+            }
 
-			bwin->SetBufferDirty(false);
+            bwin->SetBufferDirty(false);
 escape:
-			bwin->UnlockBuffer();
-		} else {
-			snooze(16000);
-		}
-	}
-	
-	return B_OK;
+            bwin->UnlockBuffer();
+        } else {
+            snooze(16000);
+        }
+    }
+    
+    return B_OK;
 }
 
-void BE_DestroyWindowFramebuffer(_THIS, SDL_Window * window) {
-	SDL_BWin *bwin = _ToBeWin(window);
-	
-	bwin->LockBuffer();
-	
-	/* Free and clear the window buffer */
-	BBitmap *bitmap = bwin->GetBitmap();
-	delete bitmap;
-	bwin->SetBitmap(NULL);
-	bwin->SetBufferExists(false);
-	bwin->UnlockBuffer();
+void HAIKU_DestroyWindowFramebuffer(_THIS, SDL_Window * window) {
+    SDL_BWin *bwin = _ToBeWin(window);
+    
+    bwin->LockBuffer();
+    
+    /* Free and clear the window buffer */
+    BBitmap *bitmap = bwin->GetBitmap();
+    delete bitmap;
+    bwin->SetBitmap(NULL);
+    bwin->SetBufferExists(false);
+    bwin->UnlockBuffer();
 }
 
 
@@ -202,51 +202,51 @@
  * solved, but I doubt it- they were pretty sporadic before now.
  */
 #ifndef DRAWTHREAD
-static int32 BE_UpdateOnce(SDL_Window *window) {
-	SDL_BWin *bwin = _ToBeWin(window);
-	BScreen bscreen;
-	if(!bscreen.IsValid()) {
-		return -1;
-	}
+static int32 HAIKU_UpdateOnce(SDL_Window *window) {
+    SDL_BWin *bwin = _ToBeWin(window);
+    BScreen bscreen;
+    if(!bscreen.IsValid()) {
+        return -1;
+    }
 
-	if(bwin->ConnectionEnabled() && bwin->Connected()) {
-		bwin->LockBuffer();
-		int32 windowPitch = window->surface->pitch;
-		int32 bufferPitch = bwin->GetRowBytes();
-		uint8 *windowpx;
-		uint8 *bufferpx;
+    if(bwin->ConnectionEnabled() && bwin->Connected()) {
+        bwin->LockBuffer();
+        int32 windowPitch = window->surface->pitch;
+        int32 bufferPitch = bwin->GetRowBytes();
+        uint8 *windowpx;
+        uint8 *bufferpx;
 
-		int32 BPP = bwin->GetBytesPerPx();
-		uint8 *windowBaseAddress = (uint8*)window->surface->pixels;
-		int32 windowSub = bwin->GetFbX() * BPP +
-						  bwin->GetFbY() * windowPitch;
-		clipping_rect *clips = bwin->GetClips();
-		int32 numClips = bwin->GetNumClips();
-		int i, y;
+        int32 BPP = bwin->GetBytesPerPx();
+        uint8 *windowBaseAddress = (uint8*)window->surface->pixels;
+        int32 windowSub = bwin->GetFbX() * BPP +
+                          bwin->GetFbY() * windowPitch;
+        clipping_rect *clips = bwin->GetClips();
+        int32 numClips = bwin->GetNumClips();
+        int i, y;
 
-		/* Blit each clipping rectangle */
-		bscreen.WaitForRetrace();
-		for(i = 0; i < numClips; ++i) {
-			/* Get addresses of the start of each clipping rectangle */
-			int32 width = clips[i].right - clips[i].left + 1;
-			int32 height = clips[i].bottom - clips[i].top + 1;
-			bufferpx = bwin->GetBufferPx() + 
-				clips[i].top * bufferPitch + clips[i].left * BPP;
-			windowpx = windowBaseAddress + 
-				clips[i].top * windowPitch + clips[i].left * BPP - windowSub;
+        /* Blit each clipping rectangle */
+        bscreen.WaitForRetrace();
+        for(i = 0; i < numClips; ++i) {
+            /* Get addresses of the start of each clipping rectangle */
+            int32 width = clips[i].right - clips[i].left + 1;
+            int32 height = clips[i].bottom - clips[i].top + 1;
+            bufferpx = bwin->GetBufferPx() + 
+                clips[i].top * bufferPitch + clips[i].left * BPP;
+            windowpx = windowBaseAddress + 
+                clips[i].top * windowPitch + clips[i].left * BPP - windowSub;
 
-			/* Copy each row of pixels from the window buffer into the frame
-			   buffer */
-			for(y = 0; y < height; ++y)
-			{
-				memcpy(bufferpx, windowpx, width * BPP);
-				bufferpx += bufferPitch;
-				windowpx += windowPitch;
-			}
-		}
-		bwin->UnlockBuffer();
-	}
-	return 0;
+            /* Copy each row of pixels from the window buffer into the frame
+               buffer */
+            for(y = 0; y < height; ++y)
+            {
+                memcpy(bufferpx, windowpx, width * BPP);
+                bufferpx += bufferPitch;
+                windowpx += windowPitch;
+            }
+        }
+        bwin->UnlockBuffer();
+    }
+    return 0;
 }
 #endif
 
diff --git a/source/src/video/haiku/SDL_bframebuffer.h b/source/src/video/haiku/SDL_bframebuffer.h
index ce0fc62..e48156d 100644
--- a/source/src/video/haiku/SDL_bframebuffer.h
+++ b/source/src/video/haiku/SDL_bframebuffer.h
@@ -30,13 +30,13 @@
 
 #include "../SDL_sysvideo.h"
 
-extern int BE_CreateWindowFramebuffer(_THIS, SDL_Window * window,
+extern int HAIKU_CreateWindowFramebuffer(_THIS, SDL_Window * window,
                                        Uint32 * format,
                                        void ** pixels, int *pitch);
-extern int BE_UpdateWindowFramebuffer(_THIS, SDL_Window * window,
+extern int HAIKU_UpdateWindowFramebuffer(_THIS, SDL_Window * window,
                                        const SDL_Rect * rects, int numrects);
-extern void BE_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
-extern int32 BE_DrawThread(void *data);
+extern void HAIKU_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
+extern int32 HAIKU_DrawThread(void *data);
 
 #ifdef __cplusplus
 }
diff --git a/source/src/video/haiku/SDL_bkeyboard.cc b/source/src/video/haiku/SDL_bkeyboard.cc
index 5c72ecf..9a8b9a4 100644
--- a/source/src/video/haiku/SDL_bkeyboard.cc
+++ b/source/src/video/haiku/SDL_bkeyboard.cc
@@ -41,144 +41,144 @@
 static SDL_Scancode keymap[KEYMAP_SIZE];
 static int8 keystate[KEYMAP_SIZE];
 
-void BE_InitOSKeymap(void) {
-		for( uint i = 0; i < SDL_TABLESIZE(keymap); ++i ) {
-			keymap[i] = SDL_SCANCODE_UNKNOWN;
-		}
+void HAIKU_InitOSKeymap(void) {
+        for( uint i = 0; i < SDL_TABLESIZE(keymap); ++i ) {
+            keymap[i] = SDL_SCANCODE_UNKNOWN;
+        }
 
-		for( uint i = 0; i < KEYMAP_SIZE; ++i ) {
-			keystate[i] = SDL_RELEASED;
-		}
+        for( uint i = 0; i < KEYMAP_SIZE; ++i ) {
+            keystate[i] = SDL_RELEASED;
+        }
 
-		keymap[0x01]		= SDL_GetScancodeFromKey(SDLK_ESCAPE);
-		keymap[B_F1_KEY]	= SDL_GetScancodeFromKey(SDLK_F1);
-		keymap[B_F2_KEY]	= SDL_GetScancodeFromKey(SDLK_F2);
-		keymap[B_F3_KEY]	= SDL_GetScancodeFromKey(SDLK_F3);
-		keymap[B_F4_KEY]	= SDL_GetScancodeFromKey(SDLK_F4);
-		keymap[B_F5_KEY]	= SDL_GetScancodeFromKey(SDLK_F5);
-		keymap[B_F6_KEY]	= SDL_GetScancodeFromKey(SDLK_F6);
-		keymap[B_F7_KEY]	= SDL_GetScancodeFromKey(SDLK_F7);
-		keymap[B_F8_KEY]	= SDL_GetScancodeFromKey(SDLK_F8);
-		keymap[B_F9_KEY]	= SDL_GetScancodeFromKey(SDLK_F9);
-		keymap[B_F10_KEY]	= SDL_GetScancodeFromKey(SDLK_F10);
-		keymap[B_F11_KEY]	= SDL_GetScancodeFromKey(SDLK_F11);
-		keymap[B_F12_KEY]	= SDL_GetScancodeFromKey(SDLK_F12);
-		keymap[B_PRINT_KEY]	= SDL_GetScancodeFromKey(SDLK_PRINTSCREEN);
-		keymap[B_SCROLL_KEY]	= SDL_GetScancodeFromKey(SDLK_SCROLLLOCK);
-		keymap[B_PAUSE_KEY]	= SDL_GetScancodeFromKey(SDLK_PAUSE);
-		keymap[0x11]		= SDL_GetScancodeFromKey(SDLK_BACKQUOTE);
-		keymap[0x12]		= SDL_GetScancodeFromKey(SDLK_1);
-		keymap[0x13]		= SDL_GetScancodeFromKey(SDLK_2);
-		keymap[0x14]		= SDL_GetScancodeFromKey(SDLK_3);
-		keymap[0x15]		= SDL_GetScancodeFromKey(SDLK_4);
-		keymap[0x16]		= SDL_GetScancodeFromKey(SDLK_5);
-		keymap[0x17]		= SDL_GetScancodeFromKey(SDLK_6);
-		keymap[0x18]		= SDL_GetScancodeFromKey(SDLK_7);
-		keymap[0x19]		= SDL_GetScancodeFromKey(SDLK_8);
-		keymap[0x1a]		= SDL_GetScancodeFromKey(SDLK_9);
-		keymap[0x1b]		= SDL_GetScancodeFromKey(SDLK_0);
-		keymap[0x1c]		= SDL_GetScancodeFromKey(SDLK_MINUS);
-		keymap[0x1d]		= SDL_GetScancodeFromKey(SDLK_EQUALS);
-		keymap[0x1e]		= SDL_GetScancodeFromKey(SDLK_BACKSPACE);
-		keymap[0x1f]		= SDL_GetScancodeFromKey(SDLK_INSERT);
-		keymap[0x20]		= SDL_GetScancodeFromKey(SDLK_HOME);
-		keymap[0x21]		= SDL_GetScancodeFromKey(SDLK_PAGEUP);
-		keymap[0x22]		= SDL_GetScancodeFromKey(SDLK_NUMLOCKCLEAR);
-		keymap[0x23]		= SDL_GetScancodeFromKey(SDLK_KP_DIVIDE);
-		keymap[0x24]		= SDL_GetScancodeFromKey(SDLK_KP_MULTIPLY);
-		keymap[0x25]		= SDL_GetScancodeFromKey(SDLK_KP_MINUS);
-		keymap[0x26]		= SDL_GetScancodeFromKey(SDLK_TAB);
-		keymap[0x27]		= SDL_GetScancodeFromKey(SDLK_q);
-		keymap[0x28]		= SDL_GetScancodeFromKey(SDLK_w);
-		keymap[0x29]		= SDL_GetScancodeFromKey(SDLK_e);
-		keymap[0x2a]		= SDL_GetScancodeFromKey(SDLK_r);
-		keymap[0x2b]		= SDL_GetScancodeFromKey(SDLK_t);
-		keymap[0x2c]		= SDL_GetScancodeFromKey(SDLK_y);
-		keymap[0x2d]		= SDL_GetScancodeFromKey(SDLK_u);
-		keymap[0x2e]		= SDL_GetScancodeFromKey(SDLK_i);
-		keymap[0x2f]		= SDL_GetScancodeFromKey(SDLK_o);
-		keymap[0x30]		= SDL_GetScancodeFromKey(SDLK_p);
-		keymap[0x31]		= SDL_GetScancodeFromKey(SDLK_LEFTBRACKET);
-		keymap[0x32]		= SDL_GetScancodeFromKey(SDLK_RIGHTBRACKET);
-		keymap[0x33]		= SDL_GetScancodeFromKey(SDLK_BACKSLASH);
-		keymap[0x34]		= SDL_GetScancodeFromKey(SDLK_DELETE);
-		keymap[0x35]		= SDL_GetScancodeFromKey(SDLK_END);
-		keymap[0x36]		= SDL_GetScancodeFromKey(SDLK_PAGEDOWN);
-		keymap[0x37]		= SDL_GetScancodeFromKey(SDLK_KP_7);
-		keymap[0x38]		= SDL_GetScancodeFromKey(SDLK_KP_8);
-		keymap[0x39]		= SDL_GetScancodeFromKey(SDLK_KP_9);
-		keymap[0x3a]		= SDL_GetScancodeFromKey(SDLK_KP_PLUS);
-		keymap[0x3b]		= SDL_GetScancodeFromKey(SDLK_CAPSLOCK);
-		keymap[0x3c]		= SDL_GetScancodeFromKey(SDLK_a);
-		keymap[0x3d]		= SDL_GetScancodeFromKey(SDLK_s);
-		keymap[0x3e]		= SDL_GetScancodeFromKey(SDLK_d);
-		keymap[0x3f]		= SDL_GetScancodeFromKey(SDLK_f);
-		keymap[0x40]		= SDL_GetScancodeFromKey(SDLK_g);
-		keymap[0x41]		= SDL_GetScancodeFromKey(SDLK_h);
-		keymap[0x42]		= SDL_GetScancodeFromKey(SDLK_j);
-		keymap[0x43]		= SDL_GetScancodeFromKey(SDLK_k);
-		keymap[0x44]		= SDL_GetScancodeFromKey(SDLK_l);
-		keymap[0x45]		= SDL_GetScancodeFromKey(SDLK_SEMICOLON);
-		keymap[0x46]		= SDL_GetScancodeFromKey(SDLK_QUOTE);
-		keymap[0x47]		= SDL_GetScancodeFromKey(SDLK_RETURN);
-		keymap[0x48]		= SDL_GetScancodeFromKey(SDLK_KP_4);
-		keymap[0x49]		= SDL_GetScancodeFromKey(SDLK_KP_5);
-		keymap[0x4a]		= SDL_GetScancodeFromKey(SDLK_KP_6);
-		keymap[0x4b]		= SDL_GetScancodeFromKey(SDLK_LSHIFT);
-		keymap[0x4c]		= SDL_GetScancodeFromKey(SDLK_z);
-		keymap[0x4d]		= SDL_GetScancodeFromKey(SDLK_x);
-		keymap[0x4e]		= SDL_GetScancodeFromKey(SDLK_c);
-		keymap[0x4f]		= SDL_GetScancodeFromKey(SDLK_v);
-		keymap[0x50]		= SDL_GetScancodeFromKey(SDLK_b);
-		keymap[0x51]		= SDL_GetScancodeFromKey(SDLK_n);
-		keymap[0x52]		= SDL_GetScancodeFromKey(SDLK_m);
-		keymap[0x53]		= SDL_GetScancodeFromKey(SDLK_COMMA);
-		keymap[0x54]		= SDL_GetScancodeFromKey(SDLK_PERIOD);
-		keymap[0x55]		= SDL_GetScancodeFromKey(SDLK_SLASH);
-		keymap[0x56]		= SDL_GetScancodeFromKey(SDLK_RSHIFT);
-		keymap[0x57]		= SDL_GetScancodeFromKey(SDLK_UP);
-		keymap[0x58]		= SDL_GetScancodeFromKey(SDLK_KP_1);
-		keymap[0x59]		= SDL_GetScancodeFromKey(SDLK_KP_2);
-		keymap[0x5a]		= SDL_GetScancodeFromKey(SDLK_KP_3);
-		keymap[0x5b]		= SDL_GetScancodeFromKey(SDLK_KP_ENTER);
-		keymap[0x5c]		= SDL_GetScancodeFromKey(SDLK_LCTRL);
-		keymap[0x5d]		= SDL_GetScancodeFromKey(SDLK_LALT);
-		keymap[0x5e]		= SDL_GetScancodeFromKey(SDLK_SPACE);
-		keymap[0x5f]		= SDL_GetScancodeFromKey(SDLK_RALT);
-		keymap[0x60]		= SDL_GetScancodeFromKey(SDLK_RCTRL);
-		keymap[0x61]		= SDL_GetScancodeFromKey(SDLK_LEFT);
-		keymap[0x62]		= SDL_GetScancodeFromKey(SDLK_DOWN);
-		keymap[0x63]		= SDL_GetScancodeFromKey(SDLK_RIGHT);
-		keymap[0x64]		= SDL_GetScancodeFromKey(SDLK_KP_0);
-		keymap[0x65]		= SDL_GetScancodeFromKey(SDLK_KP_PERIOD);
-		keymap[0x66]		= SDL_GetScancodeFromKey(SDLK_LGUI);
-		keymap[0x67]		= SDL_GetScancodeFromKey(SDLK_RGUI);
-		keymap[0x68]		= SDL_GetScancodeFromKey(SDLK_MENU);
-		keymap[0x69]		= SDL_GetScancodeFromKey(SDLK_2); /* SDLK_EURO */
-		keymap[0x6a]		= SDL_GetScancodeFromKey(SDLK_KP_EQUALS);
-		keymap[0x6b]		= SDL_GetScancodeFromKey(SDLK_POWER);
+        keymap[0x01]        = SDL_GetScancodeFromKey(SDLK_ESCAPE);
+        keymap[B_F1_KEY]    = SDL_GetScancodeFromKey(SDLK_F1);
+        keymap[B_F2_KEY]    = SDL_GetScancodeFromKey(SDLK_F2);
+        keymap[B_F3_KEY]    = SDL_GetScancodeFromKey(SDLK_F3);
+        keymap[B_F4_KEY]    = SDL_GetScancodeFromKey(SDLK_F4);
+        keymap[B_F5_KEY]    = SDL_GetScancodeFromKey(SDLK_F5);
+        keymap[B_F6_KEY]    = SDL_GetScancodeFromKey(SDLK_F6);
+        keymap[B_F7_KEY]    = SDL_GetScancodeFromKey(SDLK_F7);
+        keymap[B_F8_KEY]    = SDL_GetScancodeFromKey(SDLK_F8);
+        keymap[B_F9_KEY]    = SDL_GetScancodeFromKey(SDLK_F9);
+        keymap[B_F10_KEY]    = SDL_GetScancodeFromKey(SDLK_F10);
+        keymap[B_F11_KEY]    = SDL_GetScancodeFromKey(SDLK_F11);
+        keymap[B_F12_KEY]    = SDL_GetScancodeFromKey(SDLK_F12);
+        keymap[B_PRINT_KEY]    = SDL_GetScancodeFromKey(SDLK_PRINTSCREEN);
+        keymap[B_SCROLL_KEY]    = SDL_GetScancodeFromKey(SDLK_SCROLLLOCK);
+        keymap[B_PAUSE_KEY]    = SDL_GetScancodeFromKey(SDLK_PAUSE);
+        keymap[0x11]        = SDL_GetScancodeFromKey(SDLK_BACKQUOTE);
+        keymap[0x12]        = SDL_GetScancodeFromKey(SDLK_1);
+        keymap[0x13]        = SDL_GetScancodeFromKey(SDLK_2);
+        keymap[0x14]        = SDL_GetScancodeFromKey(SDLK_3);
+        keymap[0x15]        = SDL_GetScancodeFromKey(SDLK_4);
+        keymap[0x16]        = SDL_GetScancodeFromKey(SDLK_5);
+        keymap[0x17]        = SDL_GetScancodeFromKey(SDLK_6);
+        keymap[0x18]        = SDL_GetScancodeFromKey(SDLK_7);
+        keymap[0x19]        = SDL_GetScancodeFromKey(SDLK_8);
+        keymap[0x1a]        = SDL_GetScancodeFromKey(SDLK_9);
+        keymap[0x1b]        = SDL_GetScancodeFromKey(SDLK_0);
+        keymap[0x1c]        = SDL_GetScancodeFromKey(SDLK_MINUS);
+        keymap[0x1d]        = SDL_GetScancodeFromKey(SDLK_EQUALS);
+        keymap[0x1e]        = SDL_GetScancodeFromKey(SDLK_BACKSPACE);
+        keymap[0x1f]        = SDL_GetScancodeFromKey(SDLK_INSERT);
+        keymap[0x20]        = SDL_GetScancodeFromKey(SDLK_HOME);
+        keymap[0x21]        = SDL_GetScancodeFromKey(SDLK_PAGEUP);
+        keymap[0x22]        = SDL_GetScancodeFromKey(SDLK_NUMLOCKCLEAR);
+        keymap[0x23]        = SDL_GetScancodeFromKey(SDLK_KP_DIVIDE);
+        keymap[0x24]        = SDL_GetScancodeFromKey(SDLK_KP_MULTIPLY);
+        keymap[0x25]        = SDL_GetScancodeFromKey(SDLK_KP_MINUS);
+        keymap[0x26]        = SDL_GetScancodeFromKey(SDLK_TAB);
+        keymap[0x27]        = SDL_GetScancodeFromKey(SDLK_q);
+        keymap[0x28]        = SDL_GetScancodeFromKey(SDLK_w);
+        keymap[0x29]        = SDL_GetScancodeFromKey(SDLK_e);
+        keymap[0x2a]        = SDL_GetScancodeFromKey(SDLK_r);
+        keymap[0x2b]        = SDL_GetScancodeFromKey(SDLK_t);
+        keymap[0x2c]        = SDL_GetScancodeFromKey(SDLK_y);
+        keymap[0x2d]        = SDL_GetScancodeFromKey(SDLK_u);
+        keymap[0x2e]        = SDL_GetScancodeFromKey(SDLK_i);
+        keymap[0x2f]        = SDL_GetScancodeFromKey(SDLK_o);
+        keymap[0x30]        = SDL_GetScancodeFromKey(SDLK_p);
+        keymap[0x31]        = SDL_GetScancodeFromKey(SDLK_LEFTBRACKET);
+        keymap[0x32]        = SDL_GetScancodeFromKey(SDLK_RIGHTBRACKET);
+        keymap[0x33]        = SDL_GetScancodeFromKey(SDLK_BACKSLASH);
+        keymap[0x34]        = SDL_GetScancodeFromKey(SDLK_DELETE);
+        keymap[0x35]        = SDL_GetScancodeFromKey(SDLK_END);
+        keymap[0x36]        = SDL_GetScancodeFromKey(SDLK_PAGEDOWN);
+        keymap[0x37]        = SDL_GetScancodeFromKey(SDLK_KP_7);
+        keymap[0x38]        = SDL_GetScancodeFromKey(SDLK_KP_8);
+        keymap[0x39]        = SDL_GetScancodeFromKey(SDLK_KP_9);
+        keymap[0x3a]        = SDL_GetScancodeFromKey(SDLK_KP_PLUS);
+        keymap[0x3b]        = SDL_GetScancodeFromKey(SDLK_CAPSLOCK);
+        keymap[0x3c]        = SDL_GetScancodeFromKey(SDLK_a);
+        keymap[0x3d]        = SDL_GetScancodeFromKey(SDLK_s);
+        keymap[0x3e]        = SDL_GetScancodeFromKey(SDLK_d);
+        keymap[0x3f]        = SDL_GetScancodeFromKey(SDLK_f);
+        keymap[0x40]        = SDL_GetScancodeFromKey(SDLK_g);
+        keymap[0x41]        = SDL_GetScancodeFromKey(SDLK_h);
+        keymap[0x42]        = SDL_GetScancodeFromKey(SDLK_j);
+        keymap[0x43]        = SDL_GetScancodeFromKey(SDLK_k);
+        keymap[0x44]        = SDL_GetScancodeFromKey(SDLK_l);
+        keymap[0x45]        = SDL_GetScancodeFromKey(SDLK_SEMICOLON);
+        keymap[0x46]        = SDL_GetScancodeFromKey(SDLK_QUOTE);
+        keymap[0x47]        = SDL_GetScancodeFromKey(SDLK_RETURN);
+        keymap[0x48]        = SDL_GetScancodeFromKey(SDLK_KP_4);
+        keymap[0x49]        = SDL_GetScancodeFromKey(SDLK_KP_5);
+        keymap[0x4a]        = SDL_GetScancodeFromKey(SDLK_KP_6);
+        keymap[0x4b]        = SDL_GetScancodeFromKey(SDLK_LSHIFT);
+        keymap[0x4c]        = SDL_GetScancodeFromKey(SDLK_z);
+        keymap[0x4d]        = SDL_GetScancodeFromKey(SDLK_x);
+        keymap[0x4e]        = SDL_GetScancodeFromKey(SDLK_c);
+        keymap[0x4f]        = SDL_GetScancodeFromKey(SDLK_v);
+        keymap[0x50]        = SDL_GetScancodeFromKey(SDLK_b);
+        keymap[0x51]        = SDL_GetScancodeFromKey(SDLK_n);
+        keymap[0x52]        = SDL_GetScancodeFromKey(SDLK_m);
+        keymap[0x53]        = SDL_GetScancodeFromKey(SDLK_COMMA);
+        keymap[0x54]        = SDL_GetScancodeFromKey(SDLK_PERIOD);
+        keymap[0x55]        = SDL_GetScancodeFromKey(SDLK_SLASH);
+        keymap[0x56]        = SDL_GetScancodeFromKey(SDLK_RSHIFT);
+        keymap[0x57]        = SDL_GetScancodeFromKey(SDLK_UP);
+        keymap[0x58]        = SDL_GetScancodeFromKey(SDLK_KP_1);
+        keymap[0x59]        = SDL_GetScancodeFromKey(SDLK_KP_2);
+        keymap[0x5a]        = SDL_GetScancodeFromKey(SDLK_KP_3);
+        keymap[0x5b]        = SDL_GetScancodeFromKey(SDLK_KP_ENTER);
+        keymap[0x5c]        = SDL_GetScancodeFromKey(SDLK_LCTRL);
+        keymap[0x5d]        = SDL_GetScancodeFromKey(SDLK_LALT);
+        keymap[0x5e]        = SDL_GetScancodeFromKey(SDLK_SPACE);
+        keymap[0x5f]        = SDL_GetScancodeFromKey(SDLK_RALT);
+        keymap[0x60]        = SDL_GetScancodeFromKey(SDLK_RCTRL);
+        keymap[0x61]        = SDL_GetScancodeFromKey(SDLK_LEFT);
+        keymap[0x62]        = SDL_GetScancodeFromKey(SDLK_DOWN);
+        keymap[0x63]        = SDL_GetScancodeFromKey(SDLK_RIGHT);
+        keymap[0x64]        = SDL_GetScancodeFromKey(SDLK_KP_0);
+        keymap[0x65]        = SDL_GetScancodeFromKey(SDLK_KP_PERIOD);
+        keymap[0x66]        = SDL_GetScancodeFromKey(SDLK_LGUI);
+        keymap[0x67]        = SDL_GetScancodeFromKey(SDLK_RGUI);
+        keymap[0x68]        = SDL_GetScancodeFromKey(SDLK_MENU);
+        keymap[0x69]        = SDL_GetScancodeFromKey(SDLK_2); /* SDLK_EURO */
+        keymap[0x6a]        = SDL_GetScancodeFromKey(SDLK_KP_EQUALS);
+        keymap[0x6b]        = SDL_GetScancodeFromKey(SDLK_POWER);
 }
 
-SDL_Scancode BE_GetScancodeFromBeKey(int32 bkey) {
-	if(bkey > 0 && bkey < (int32)SDL_TABLESIZE(keymap)) {
-		return keymap[bkey];
-	} else {
-		return SDL_SCANCODE_UNKNOWN;
-	}
+SDL_Scancode HAIKU_GetScancodeFromBeKey(int32 bkey) {
+    if(bkey > 0 && bkey < (int32)SDL_TABLESIZE(keymap)) {
+        return keymap[bkey];
+    } else {
+        return SDL_SCANCODE_UNKNOWN;
+    }
 }
 
-int8 BE_GetKeyState(int32 bkey) {
-	if(bkey > 0 && bkey < KEYMAP_SIZE) {
-		return keystate[bkey];
-	} else {
-		return SDL_RELEASED;
-	}
+int8 HAIKU_GetKeyState(int32 bkey) {
+    if(bkey > 0 && bkey < KEYMAP_SIZE) {
+        return keystate[bkey];
+    } else {
+        return SDL_RELEASED;
+    }
 }
 
-void BE_SetKeyState(int32 bkey, int8 state) {
-	if(bkey > 0 && bkey < KEYMAP_SIZE) {
-		keystate[bkey] = state;
-	}
+void HAIKU_SetKeyState(int32 bkey, int8 state) {
+    if(bkey > 0 && bkey < KEYMAP_SIZE) {
+        keystate[bkey] = state;
+    }
 }
 
 #ifdef __cplusplus
diff --git a/source/src/video/haiku/SDL_bkeyboard.h b/source/src/video/haiku/SDL_bkeyboard.h
index 9620c9b..0184828 100644
--- a/source/src/video/haiku/SDL_bkeyboard.h
+++ b/source/src/video/haiku/SDL_bkeyboard.h
@@ -30,10 +30,10 @@
 
 #include "../../../include/SDL_keyboard.h"
 
-extern void BE_InitOSKeymap(void);
-extern SDL_Scancode BE_GetScancodeFromBeKey(int32 bkey);
-extern int8 BE_GetKeyState(int32 bkey);
-extern void BE_SetKeyState(int32 bkey, int8 state);
+extern void HAIKU_InitOSKeymap(void);
+extern SDL_Scancode HAIKU_GetScancodeFromBeKey(int32 bkey);
+extern int8 HAIKU_GetKeyState(int32 bkey);
+extern void HAIKU_SetKeyState(int32 bkey, int8 state);
 
 #ifdef __cplusplus
 }
diff --git a/source/src/video/haiku/SDL_bmodes.cc b/source/src/video/haiku/SDL_bmodes.cc
index 1105342..9d71996 100644
--- a/source/src/video/haiku/SDL_bmodes.cc
+++ b/source/src/video/haiku/SDL_bmodes.cc
@@ -44,30 +44,30 @@
 /* This wrapper is here so that the driverdata can be freed without freeing
    the display_mode structure */
 struct SDL_DisplayModeData {
-	display_mode *bmode;
+    display_mode *bmode;
 };
 #endif
 
 static SDL_INLINE SDL_BWin *_ToBeWin(SDL_Window *window) {
-	return ((SDL_BWin*)(window->driverdata));
+    return ((SDL_BWin*)(window->driverdata));
 }
 
 static SDL_INLINE SDL_BApp *_GetBeApp() {
-	return ((SDL_BApp*)be_app);
+    return ((SDL_BApp*)be_app);
 }
 
 static SDL_INLINE display_mode * _ExtractBMode(SDL_DisplayMode *mode) {
 #if WRAP_BMODE
-	return ((SDL_DisplayModeData*)mode->driverdata)->bmode;
+    return ((SDL_DisplayModeData*)mode->driverdata)->bmode;
 #else
-	return (display_mode*)(mode->driverdata);
+    return (display_mode*)(mode->driverdata);
 #endif
 }
 
 /* Copied from haiku/trunk/src/preferences/screen/ScreenMode.cpp */
 static float get_refresh_rate(display_mode &mode) {
-	return float(mode.timing.pixel_clock * 1000)
-		/ float(mode.timing.h_total * mode.timing.v_total);
+    return float(mode.timing.pixel_clock * 1000)
+        / float(mode.timing.h_total * mode.timing.v_total);
 }
 
 
@@ -76,252 +76,252 @@
  * This is a useful debugging tool.  Uncomment and insert into code as needed.
  */
 void _SpoutModeData(display_mode *bmode) {
-	printf("BMode:\n");
-	printf("\tw,h = (%i,%i)\n", bmode->virtual_width, bmode->virtual_height);
-	printf("\th,v = (%i,%i)\n", bmode->h_display_start, 
-			bmode->v_display_start);
-	if(bmode->flags) {
-		printf("\tFlags:\n");
-		if(bmode->flags & B_SCROLL) {
-			printf("\t\tB_SCROLL\n");
-		}
-		if(bmode->flags & B_8_BIT_DAC) {
-			printf("\t\tB_8_BIT_DAC\n");
-		}
-		if(bmode->flags & B_HARDWARE_CURSOR) {
-			printf("\t\tB_HARDWARE_CURSOR\n");
-		}
-		if(bmode->flags & B_PARALLEL_ACCESS) {
-			printf("\t\tB_PARALLEL_ACCESS\n");
-		}
-		if(bmode->flags & B_DPMS) {
-			printf("\t\tB_DPMS\n");
-		}
-		if(bmode->flags & B_IO_FB_NA) {
-			printf("\t\tB_IO_FB_NA\n");
-		}
-	}
-	printf("\tTiming:\n");
-	printf("\t\tpx clock: %i\n", bmode->timing.pixel_clock);
-	printf("\t\th - display: %i sync start: %i sync end: %i total: %i\n",
-		bmode->timing.h_display, bmode->timing.h_sync_start,
-		bmode->timing.h_sync_end, bmode->timing.h_total);
-	printf("\t\tv - display: %i sync start: %i sync end: %i total: %i\n",
-		bmode->timing.v_display, bmode->timing.v_sync_start,
-		bmode->timing.v_sync_end, bmode->timing.v_total);
-	if(bmode->timing.flags) {
-		printf("\t\tFlags:\n");
-		if(bmode->timing.flags & B_BLANK_PEDESTAL) {
-			printf("\t\t\tB_BLANK_PEDESTAL\n");
-		}
-		if(bmode->timing.flags & B_TIMING_INTERLACED) {
-			printf("\t\t\tB_TIMING_INTERLACED\n");
-		}
-		if(bmode->timing.flags & B_POSITIVE_HSYNC) {
-			printf("\t\t\tB_POSITIVE_HSYNC\n");
-		}
-		if(bmode->timing.flags & B_POSITIVE_VSYNC) {
-			printf("\t\t\tB_POSITIVE_VSYNC\n");
-		}
-		if(bmode->timing.flags & B_SYNC_ON_GREEN) {
-			printf("\t\t\tB_SYNC_ON_GREEN\n");
-		}
-	}
+    printf("BMode:\n");
+    printf("\tw,h = (%i,%i)\n", bmode->virtual_width, bmode->virtual_height);
+    printf("\th,v = (%i,%i)\n", bmode->h_display_start, 
+            bmode->v_display_start);
+    if(bmode->flags) {
+        printf("\tFlags:\n");
+        if(bmode->flags & B_SCROLL) {
+            printf("\t\tB_SCROLL\n");
+        }
+        if(bmode->flags & B_8_BIT_DAC) {
+            printf("\t\tB_8_BIT_DAC\n");
+        }
+        if(bmode->flags & B_HARDWARE_CURSOR) {
+            printf("\t\tB_HARDWARE_CURSOR\n");
+        }
+        if(bmode->flags & B_PARALLEL_ACCESS) {
+            printf("\t\tB_PARALLEL_ACCESS\n");
+        }
+        if(bmode->flags & B_DPMS) {
+            printf("\t\tB_DPMS\n");
+        }
+        if(bmode->flags & B_IO_FB_NA) {
+            printf("\t\tB_IO_FB_NA\n");
+        }
+    }
+    printf("\tTiming:\n");
+    printf("\t\tpx clock: %i\n", bmode->timing.pixel_clock);
+    printf("\t\th - display: %i sync start: %i sync end: %i total: %i\n",
+        bmode->timing.h_display, bmode->timing.h_sync_start,
+        bmode->timing.h_sync_end, bmode->timing.h_total);
+    printf("\t\tv - display: %i sync start: %i sync end: %i total: %i\n",
+        bmode->timing.v_display, bmode->timing.v_sync_start,
+        bmode->timing.v_sync_end, bmode->timing.v_total);
+    if(bmode->timing.flags) {
+        printf("\t\tFlags:\n");
+        if(bmode->timing.flags & B_BLANK_PEDESTAL) {
+            printf("\t\t\tB_BLANK_PEDESTAL\n");
+        }
+        if(bmode->timing.flags & B_TIMING_INTERLACED) {
+            printf("\t\t\tB_TIMING_INTERLACED\n");
+        }
+        if(bmode->timing.flags & B_POSITIVE_HSYNC) {
+            printf("\t\t\tB_POSITIVE_HSYNC\n");
+        }
+        if(bmode->timing.flags & B_POSITIVE_VSYNC) {
+            printf("\t\t\tB_POSITIVE_VSYNC\n");
+        }
+        if(bmode->timing.flags & B_SYNC_ON_GREEN) {
+            printf("\t\t\tB_SYNC_ON_GREEN\n");
+        }
+    }
 }
 #endif
 
 
 
-int32 BE_ColorSpaceToBitsPerPixel(uint32 colorspace)
+int32 HAIKU_ColorSpaceToBitsPerPixel(uint32 colorspace)
 {
-	int bitsperpixel;
+    int bitsperpixel;
 
-	bitsperpixel = 0;
-	switch (colorspace) {
-	    case B_CMAP8:
-		bitsperpixel = 8;
-		break;
-	    case B_RGB15:
-	    case B_RGBA15:
-	    case B_RGB15_BIG:
-	    case B_RGBA15_BIG:
-		bitsperpixel = 15;
-		break;
-	    case B_RGB16:
-	    case B_RGB16_BIG:
-		bitsperpixel = 16;
-		break;
-	    case B_RGB32:
-	    case B_RGBA32:
-	    case B_RGB32_BIG:
-	    case B_RGBA32_BIG:
-		bitsperpixel = 32;
-		break;
-	    default:
-		break;
-	}
-	return(bitsperpixel);
+    bitsperpixel = 0;
+    switch (colorspace) {
+        case B_CMAP8:
+        bitsperpixel = 8;
+        break;
+        case B_RGB15:
+        case B_RGBA15:
+        case B_RGB15_BIG:
+        case B_RGBA15_BIG:
+        bitsperpixel = 15;
+        break;
+        case B_RGB16:
+        case B_RGB16_BIG:
+        bitsperpixel = 16;
+        break;
+        case B_RGB32:
+        case B_RGBA32:
+        case B_RGB32_BIG:
+        case B_RGBA32_BIG:
+        bitsperpixel = 32;
+        break;
+        default:
+        break;
+    }
+    return(bitsperpixel);
 }
 
-int32 BE_BPPToSDLPxFormat(int32 bpp) {
-	/* Translation taken from SDL_windowsmodes.c */
-	switch (bpp) {
-	case 32:
-		return SDL_PIXELFORMAT_RGB888;
-		break;
-	case 24:	/* May not be supported by Haiku */
-		return SDL_PIXELFORMAT_RGB24;
-		break;
-	case 16:
-		return SDL_PIXELFORMAT_RGB565;
-		break;
-	case 15:
-		return SDL_PIXELFORMAT_RGB555;
-		break;
-	case 8:
-		return SDL_PIXELFORMAT_INDEX8;
-		break;
-	case 4:		/* May not be supported by Haiku */
-		return SDL_PIXELFORMAT_INDEX4LSB;
-		break;
-	}
+int32 HAIKU_BPPToSDLPxFormat(int32 bpp) {
+    /* Translation taken from SDL_windowsmodes.c */
+    switch (bpp) {
+    case 32:
+        return SDL_PIXELFORMAT_RGB888;
+        break;
+    case 24:    /* May not be supported by Haiku */
+        return SDL_PIXELFORMAT_RGB24;
+        break;
+    case 16:
+        return SDL_PIXELFORMAT_RGB565;
+        break;
+    case 15:
+        return SDL_PIXELFORMAT_RGB555;
+        break;
+    case 8:
+        return SDL_PIXELFORMAT_INDEX8;
+        break;
+    case 4:        /* May not be supported by Haiku */
+        return SDL_PIXELFORMAT_INDEX4LSB;
+        break;
+    }
 
-	/* May never get here, but safer and needed to shut up compiler */
-	SDL_SetError("Invalid bpp value");
-	return 0;       
+    /* May never get here, but safer and needed to shut up compiler */
+    SDL_SetError("Invalid bpp value");
+    return 0;       
 }
 
 static void _BDisplayModeToSdlDisplayMode(display_mode *bmode,
-		SDL_DisplayMode *mode) {
-	mode->w = bmode->virtual_width;
-	mode->h = bmode->virtual_height;
-	mode->refresh_rate = (int)get_refresh_rate(*bmode);
+        SDL_DisplayMode *mode) {
+    mode->w = bmode->virtual_width;
+    mode->h = bmode->virtual_height;
+    mode->refresh_rate = (int)get_refresh_rate(*bmode);
 
 #if WRAP_BMODE
-	SDL_DisplayModeData *data = (SDL_DisplayModeData*)SDL_calloc(1,
-		sizeof(SDL_DisplayModeData));
-	data->bmode = bmode;
-	
-	mode->driverdata = data;
+    SDL_DisplayModeData *data = (SDL_DisplayModeData*)SDL_calloc(1,
+        sizeof(SDL_DisplayModeData));
+    data->bmode = bmode;
+    
+    mode->driverdata = data;
 
 #else
 
-	mode->driverdata = bmode;
+    mode->driverdata = bmode;
 #endif
 
-	/* Set the format */
-	int32 bpp = BE_ColorSpaceToBitsPerPixel(bmode->space);
-	mode->format = BE_BPPToSDLPxFormat(bpp);
+    /* Set the format */
+    int32 bpp = HAIKU_ColorSpaceToBitsPerPixel(bmode->space);
+    mode->format = HAIKU_BPPToSDLPxFormat(bpp);
 }
 
 /* Later, there may be more than one monitor available */
 static void _AddDisplay(BScreen *screen) {
-	SDL_VideoDisplay display;
-	SDL_DisplayMode *mode = (SDL_DisplayMode*)SDL_calloc(1,
-		sizeof(SDL_DisplayMode));
-	display_mode *bmode = (display_mode*)SDL_calloc(1, sizeof(display_mode));
-	screen->GetMode(bmode);
+    SDL_VideoDisplay display;
+    SDL_DisplayMode *mode = (SDL_DisplayMode*)SDL_calloc(1,
+        sizeof(SDL_DisplayMode));
+    display_mode *bmode = (display_mode*)SDL_calloc(1, sizeof(display_mode));
+    screen->GetMode(bmode);
 
-	_BDisplayModeToSdlDisplayMode(bmode, mode);
-	
-	SDL_zero(display);
-	display.desktop_mode = *mode;
-	display.current_mode = *mode;
-	
-	SDL_AddVideoDisplay(&display);
+    _BDisplayModeToSdlDisplayMode(bmode, mode);
+    
+    SDL_zero(display);
+    display.desktop_mode = *mode;
+    display.current_mode = *mode;
+    
+    SDL_AddVideoDisplay(&display);
 }
 
 /*
  * Functions called by SDL
  */
 
-int BE_InitModes(_THIS) {
-	BScreen screen;
+int HAIKU_InitModes(_THIS) {
+    BScreen screen;
 
-	/* TODO: When Haiku supports multiple display screens, call
-	   _AddDisplayScreen() for each of them. */
-	_AddDisplay(&screen);
-	return 0;
+    /* TODO: When Haiku supports multiple display screens, call
+       _AddDisplayScreen() for each of them. */
+    _AddDisplay(&screen);
+    return 0;
 }
 
-int BE_QuitModes(_THIS) {
-	/* FIXME: Nothing really needs to be done here at the moment? */
-	return 0;
+int HAIKU_QuitModes(_THIS) {
+    /* FIXME: Nothing really needs to be done here at the moment? */
+    return 0;
 }
 
 
-int BE_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect) {
-	BScreen bscreen;
-	BRect rc = bscreen.Frame();
-	rect->x = (int)rc.left;
-	rect->y = (int)rc.top;
-	rect->w = (int)rc.Width() + 1;
-	rect->h = (int)rc.Height() + 1;
-	return 0;
+int HAIKU_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *rect) {
+    BScreen bscreen;
+    BRect rc = bscreen.Frame();
+    rect->x = (int)rc.left;
+    rect->y = (int)rc.top;
+    rect->w = (int)rc.Width() + 1;
+    rect->h = (int)rc.Height() + 1;
+    return 0;
 }
 
-void BE_GetDisplayModes(_THIS, SDL_VideoDisplay *display) {
-	/* Get the current screen */
-	BScreen bscreen;
+void HAIKU_GetDisplayModes(_THIS, SDL_VideoDisplay *display) {
+    /* Get the current screen */
+    BScreen bscreen;
 
-	/* Iterate through all of the modes */
-	SDL_DisplayMode mode;
-	display_mode this_bmode;
-	display_mode *bmodes;
-	uint32 count, i;
-	
-	/* Get graphics-hardware supported modes */
-	bscreen.GetModeList(&bmodes, &count);
-	bscreen.GetMode(&this_bmode);
-	
-	for(i = 0; i < count; ++i) {
-		// FIXME: Apparently there are errors with colorspace changes
-		if (bmodes[i].space == this_bmode.space) {
-			_BDisplayModeToSdlDisplayMode(&bmodes[i], &mode);
-			SDL_AddDisplayMode(display, &mode);
-		}
-	}
-	free(bmodes);
+    /* Iterate through all of the modes */
+    SDL_DisplayMode mode;
+    display_mode this_bmode;
+    display_mode *bmodes;
+    uint32 count, i;
+    
+    /* Get graphics-hardware supported modes */
+    bscreen.GetModeList(&bmodes, &count);
+    bscreen.GetMode(&this_bmode);
+    
+    for(i = 0; i < count; ++i) {
+        // FIXME: Apparently there are errors with colorspace changes
+        if (bmodes[i].space == this_bmode.space) {
+            _BDisplayModeToSdlDisplayMode(&bmodes[i], &mode);
+            SDL_AddDisplayMode(display, &mode);
+        }
+    }
+    free(bmodes);
 }
 
 
-int BE_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode){
-	/* Get the current screen */
-	BScreen bscreen;
-	if(!bscreen.IsValid()) {
-		printf(__FILE__": %d - ERROR: BAD SCREEN\n", __LINE__);
-	}
+int HAIKU_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode){
+    /* Get the current screen */
+    BScreen bscreen;
+    if(!bscreen.IsValid()) {
+        printf(__FILE__": %d - ERROR: BAD SCREEN\n", __LINE__);
+    }
 
-	/* Set the mode using the driver data */
-	display_mode *bmode = _ExtractBMode(mode);
+    /* Set the mode using the driver data */
+    display_mode *bmode = _ExtractBMode(mode);
 
 
-	/* FIXME: Is the first option always going to be the right one? */
-	uint32 c = 0, i;
-	display_mode *bmode_list;
-	bscreen.GetModeList(&bmode_list, &c);
-	for(i = 0; i < c; ++i) {
-		if(	bmode_list[i].space == bmode->space &&
-			bmode_list[i].virtual_width == bmode->virtual_width &&
-			bmode_list[i].virtual_height == bmode->virtual_height ) {
-				bmode = &bmode_list[i];
-				break;
-		}
-	}
+    /* FIXME: Is the first option always going to be the right one? */
+    uint32 c = 0, i;
+    display_mode *bmode_list;
+    bscreen.GetModeList(&bmode_list, &c);
+    for(i = 0; i < c; ++i) {
+        if(    bmode_list[i].space == bmode->space &&
+            bmode_list[i].virtual_width == bmode->virtual_width &&
+            bmode_list[i].virtual_height == bmode->virtual_height ) {
+                bmode = &bmode_list[i];
+                break;
+        }
+    }
 
-	if(bscreen.SetMode(bmode) != B_OK) {
-		return SDL_SetError("Bad video mode");
-	}
-	
-	free(bmode_list);
-	
+    if(bscreen.SetMode(bmode) != B_OK) {
+        return SDL_SetError("Bad video mode");
+    }
+    
+    free(bmode_list);
+    
 #if SDL_VIDEO_OPENGL
-	/* FIXME: Is there some way to reboot the OpenGL context?  This doesn't
-	   help */
-//	BE_GL_RebootContexts(_this);
+    /* FIXME: Is there some way to reboot the OpenGL context?  This doesn't
+       help */
+//    HAIKU_GL_RebootContexts(_this);
 #endif
 
-	return 0;
+    return 0;
 }
 
 #ifdef __cplusplus
diff --git a/source/src/video/haiku/SDL_bmodes.h b/source/src/video/haiku/SDL_bmodes.h
index 38f4b58..3abc1dc 100644
--- a/source/src/video/haiku/SDL_bmodes.h
+++ b/source/src/video/haiku/SDL_bmodes.h
@@ -28,15 +28,15 @@
 
 #include "../SDL_sysvideo.h"
 
-extern int32 BE_ColorSpaceToBitsPerPixel(uint32 colorspace);
-extern int32 BE_BPPToSDLPxFormat(int32 bpp);
+extern int32 HAIKU_ColorSpaceToBitsPerPixel(uint32 colorspace);
+extern int32 HAIKU_BPPToSDLPxFormat(int32 bpp);
 
-extern int BE_InitModes(_THIS);
-extern int BE_QuitModes(_THIS);
-extern int BE_GetDisplayBounds(_THIS, SDL_VideoDisplay *display,
+extern int HAIKU_InitModes(_THIS);
+extern int HAIKU_QuitModes(_THIS);
+extern int HAIKU_GetDisplayBounds(_THIS, SDL_VideoDisplay *display,
     SDL_Rect *rect);
-extern void BE_GetDisplayModes(_THIS, SDL_VideoDisplay *display);
-extern int BE_SetDisplayMode(_THIS, SDL_VideoDisplay *display,
+extern void HAIKU_GetDisplayModes(_THIS, SDL_VideoDisplay *display);
+extern int HAIKU_SetDisplayMode(_THIS, SDL_VideoDisplay *display,
     SDL_DisplayMode *mode);
 
 #ifdef __cplusplus
diff --git a/source/src/video/haiku/SDL_bopengl.cc b/source/src/video/haiku/SDL_bopengl.cc
index 3456932..e599062 100644
--- a/source/src/video/haiku/SDL_bopengl.cc
+++ b/source/src/video/haiku/SDL_bopengl.cc
@@ -44,7 +44,7 @@
 }
 
 /* Passing a NULL path means load pointers from the application */
-int BE_GL_LoadLibrary(_THIS, const char *path)
+int HAIKU_GL_LoadLibrary(_THIS, const char *path)
 {
 /* FIXME: Is this working correctly? */
     image_info info;
@@ -63,7 +63,7 @@
     return 0;
 }
 
-void *BE_GL_GetProcAddress(_THIS, const char *proc)
+void *HAIKU_GL_GetProcAddress(_THIS, const char *proc)
 {
     if (_this->gl_config.dll_handle != NULL) {
         void *location = NULL;
@@ -86,19 +86,19 @@
 
 
 
-int BE_GL_SwapWindow(_THIS, SDL_Window * window) {
+int HAIKU_GL_SwapWindow(_THIS, SDL_Window * window) {
     _ToBeWin(window)->SwapBuffers();
     return 0;
 }
 
-int BE_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) {
+int HAIKU_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) {
     SDL_BWin* win = (SDL_BWin*)context;
     _GetBeApp()->SetCurrentContext(win ? win->GetGLView() : NULL);
     return 0;
 }
 
 
-SDL_GLContext BE_GL_CreateContext(_THIS, SDL_Window * window) {
+SDL_GLContext HAIKU_GL_CreateContext(_THIS, SDL_Window * window) {
     /* FIXME: Not sure what flags should be included here; may want to have
        most of them */
     SDL_BWin *bwin = _ToBeWin(window);
@@ -127,24 +127,24 @@
     return (SDL_GLContext)(bwin);
 }
 
-void BE_GL_DeleteContext(_THIS, SDL_GLContext context) {
+void HAIKU_GL_DeleteContext(_THIS, SDL_GLContext context) {
     /* Currently, automatically unlocks the view */
     ((SDL_BWin*)context)->RemoveGLView();
 }
 
 
-int BE_GL_SetSwapInterval(_THIS, int interval) {
+int HAIKU_GL_SetSwapInterval(_THIS, int interval) {
     /* TODO: Implement this, if necessary? */
     return SDL_Unsupported();
 }
 
-int BE_GL_GetSwapInterval(_THIS) {
+int HAIKU_GL_GetSwapInterval(_THIS) {
     /* TODO: Implement this, if necessary? */
     return 0;
 }
 
 
-void BE_GL_UnloadLibrary(_THIS) {
+void HAIKU_GL_UnloadLibrary(_THIS) {
     /* TODO: Implement this, if necessary? */
 }
 
@@ -152,7 +152,7 @@
 /* FIXME: This function is meant to clear the OpenGL context when the video
    mode changes (see SDL_bmodes.cc), but it doesn't seem to help, and is not
    currently in use. */
-void BE_GL_RebootContexts(_THIS) {
+void HAIKU_GL_RebootContexts(_THIS) {
     SDL_Window *window = _this->windows;
     while(window) {
         SDL_BWin *bwin = _ToBeWin(window);
diff --git a/source/src/video/haiku/SDL_bopengl.h b/source/src/video/haiku/SDL_bopengl.h
index bc0798c..b5b0de6 100644
--- a/source/src/video/haiku/SDL_bopengl.h
+++ b/source/src/video/haiku/SDL_bopengl.h
@@ -31,18 +31,18 @@
 #include "../SDL_sysvideo.h"
 
 
-extern int BE_GL_LoadLibrary(_THIS, const char *path);                  /* FIXME */
-extern void *BE_GL_GetProcAddress(_THIS, const char *proc);             /* FIXME */
-extern void BE_GL_UnloadLibrary(_THIS);                                 /* TODO */
-extern int BE_GL_MakeCurrent(_THIS, SDL_Window * window,
+extern int HAIKU_GL_LoadLibrary(_THIS, const char *path);                  /* FIXME */
+extern void *HAIKU_GL_GetProcAddress(_THIS, const char *proc);             /* FIXME */
+extern void HAIKU_GL_UnloadLibrary(_THIS);                                 /* TODO */
+extern int HAIKU_GL_MakeCurrent(_THIS, SDL_Window * window,
                               SDL_GLContext context);
-extern int BE_GL_SetSwapInterval(_THIS, int interval);                  /* TODO */
-extern int BE_GL_GetSwapInterval(_THIS);                                /* TODO */
-extern int BE_GL_SwapWindow(_THIS, SDL_Window * window);
-extern SDL_GLContext BE_GL_CreateContext(_THIS, SDL_Window * window);
-extern void BE_GL_DeleteContext(_THIS, SDL_GLContext context);
+extern int HAIKU_GL_SetSwapInterval(_THIS, int interval);                  /* TODO */
+extern int HAIKU_GL_GetSwapInterval(_THIS);                                /* TODO */
+extern int HAIKU_GL_SwapWindow(_THIS, SDL_Window * window);
+extern SDL_GLContext HAIKU_GL_CreateContext(_THIS, SDL_Window * window);
+extern void HAIKU_GL_DeleteContext(_THIS, SDL_GLContext context);
 
-extern void BE_GL_RebootContexts(_THIS);
+extern void HAIKU_GL_RebootContexts(_THIS);
 
 #ifdef __cplusplus
 }
diff --git a/source/src/video/haiku/SDL_bvideo.cc b/source/src/video/haiku/SDL_bvideo.cc
index afe20e3..e7b4b6e 100644
--- a/source/src/video/haiku/SDL_bvideo.cc
+++ b/source/src/video/haiku/SDL_bvideo.cc
@@ -37,17 +37,17 @@
 #include "SDL_bevents.h"
 
 /* FIXME: Undefined functions */
-//    #define BE_PumpEvents NULL
-    #define BE_StartTextInput NULL
-    #define BE_StopTextInput NULL
-    #define BE_SetTextInputRect NULL
+//    #define HAIKU_PumpEvents NULL
+    #define HAIKU_StartTextInput NULL
+    #define HAIKU_StopTextInput NULL
+    #define HAIKU_SetTextInputRect NULL
 
-//    #define BE_DeleteDevice NULL
+//    #define HAIKU_DeleteDevice NULL
 
 /* End undefined functions */
 
 static SDL_VideoDevice *
-BE_CreateDevice(int devindex)
+HAIKU_CreateDevice(int devindex)
 {
     SDL_VideoDevice *device;
     /*SDL_VideoData *data;*/
@@ -56,115 +56,114 @@
     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
 
     device->driverdata = NULL; /* FIXME: Is this the cause of some of the
-    							  SDL_Quit() errors? */
+                                  SDL_Quit() errors? */
 
 /* TODO: Figure out if any initialization needs to go here */
 
     /* Set the function pointers */
-    device->VideoInit = BE_VideoInit;
-    device->VideoQuit = BE_VideoQuit;
-    device->GetDisplayBounds = BE_GetDisplayBounds;
-    device->GetDisplayModes = BE_GetDisplayModes;
-    device->SetDisplayMode = BE_SetDisplayMode;
-    device->PumpEvents = BE_PumpEvents;
+    device->VideoInit = HAIKU_VideoInit;
+    device->VideoQuit = HAIKU_VideoQuit;
+    device->GetDisplayBounds = HAIKU_GetDisplayBounds;
+    device->GetDisplayModes = HAIKU_GetDisplayModes;
+    device->SetDisplayMode = HAIKU_SetDisplayMode;
+    device->PumpEvents = HAIKU_PumpEvents;
 
-    device->CreateSDLWindow = BE_CreateWindow;
-    device->CreateSDLWindowFrom = BE_CreateWindowFrom;
-    device->SetWindowTitle = BE_SetWindowTitle;
-    device->SetWindowIcon = BE_SetWindowIcon;
-    device->SetWindowPosition = BE_SetWindowPosition;
-    device->SetWindowSize = BE_SetWindowSize;
-    device->ShowWindow = BE_ShowWindow;
-    device->HideWindow = BE_HideWindow;
-    device->RaiseWindow = BE_RaiseWindow;
-    device->MaximizeWindow = BE_MaximizeWindow;
-    device->MinimizeWindow = BE_MinimizeWindow;
-    device->RestoreWindow = BE_RestoreWindow;
-    device->SetWindowBordered = BE_SetWindowBordered;
-    device->SetWindowResizable = BE_SetWindowResizable;
-    device->SetWindowFullscreen = BE_SetWindowFullscreen;
-    device->SetWindowGammaRamp = BE_SetWindowGammaRamp;
-    device->GetWindowGammaRamp = BE_GetWindowGammaRamp;
-    device->SetWindowGrab = BE_SetWindowGrab;
-    device->DestroyWindow = BE_DestroyWindow;
-    device->GetWindowWMInfo = BE_GetWindowWMInfo;
-    device->CreateWindowFramebuffer = BE_CreateWindowFramebuffer;
-    device->UpdateWindowFramebuffer = BE_UpdateWindowFramebuffer;
-    device->DestroyWindowFramebuffer = BE_DestroyWindowFramebuffer;
+    device->CreateSDLWindow = HAIKU_CreateWindow;
+    device->CreateSDLWindowFrom = HAIKU_CreateWindowFrom;
+    device->SetWindowTitle = HAIKU_SetWindowTitle;
+    device->SetWindowIcon = HAIKU_SetWindowIcon;
+    device->SetWindowPosition = HAIKU_SetWindowPosition;
+    device->SetWindowSize = HAIKU_SetWindowSize;
+    device->ShowWindow = HAIKU_ShowWindow;
+    device->HideWindow = HAIKU_HideWindow;
+    device->RaiseWindow = HAIKU_RaiseWindow;
+    device->MaximizeWindow = HAIKU_MaximizeWindow;
+    device->MinimizeWindow = HAIKU_MinimizeWindow;
+    device->RestoreWindow = HAIKU_RestoreWindow;
+    device->SetWindowBordered = HAIKU_SetWindowBordered;
+    device->SetWindowResizable = HAIKU_SetWindowResizable;
+    device->SetWindowFullscreen = HAIKU_SetWindowFullscreen;
+    device->SetWindowGammaRamp = HAIKU_SetWindowGammaRamp;
+    device->GetWindowGammaRamp = HAIKU_GetWindowGammaRamp;
+    device->SetWindowGrab = HAIKU_SetWindowGrab;
+    device->DestroyWindow = HAIKU_DestroyWindow;
+    device->GetWindowWMInfo = HAIKU_GetWindowWMInfo;
+    device->CreateWindowFramebuffer = HAIKU_CreateWindowFramebuffer;
+    device->UpdateWindowFramebuffer = HAIKU_UpdateWindowFramebuffer;
+    device->DestroyWindowFramebuffer = HAIKU_DestroyWindowFramebuffer;
     
     device->shape_driver.CreateShaper = NULL;
     device->shape_driver.SetWindowShape = NULL;
     device->shape_driver.ResizeWindowShape = NULL;
 
 #if SDL_VIDEO_OPENGL
-    device->GL_LoadLibrary = BE_GL_LoadLibrary;
-    device->GL_GetProcAddress = BE_GL_GetProcAddress;
-    device->GL_UnloadLibrary = BE_GL_UnloadLibrary;
-    device->GL_CreateContext = BE_GL_CreateContext;
-    device->GL_MakeCurrent = BE_GL_MakeCurrent;
-    device->GL_SetSwapInterval = BE_GL_SetSwapInterval;
-    device->GL_GetSwapInterval = BE_GL_GetSwapInterval;
-    device->GL_SwapWindow = BE_GL_SwapWindow;
-    device->GL_DeleteContext = BE_GL_DeleteContext;
+    device->GL_LoadLibrary = HAIKU_GL_LoadLibrary;
+    device->GL_GetProcAddress = HAIKU_GL_GetProcAddress;
+    device->GL_UnloadLibrary = HAIKU_GL_UnloadLibrary;
+    device->GL_CreateContext = HAIKU_GL_CreateContext;
+    device->GL_MakeCurrent = HAIKU_GL_MakeCurrent;
+    device->GL_SetSwapInterval = HAIKU_GL_SetSwapInterval;
+    device->GL_GetSwapInterval = HAIKU_GL_GetSwapInterval;
+    device->GL_SwapWindow = HAIKU_GL_SwapWindow;
+    device->GL_DeleteContext = HAIKU_GL_DeleteContext;
 #endif
 
-    device->StartTextInput = BE_StartTextInput;
-    device->StopTextInput = BE_StopTextInput;
-    device->SetTextInputRect = BE_SetTextInputRect;
+    device->StartTextInput = HAIKU_StartTextInput;
+    device->StopTextInput = HAIKU_StopTextInput;
+    device->SetTextInputRect = HAIKU_SetTextInputRect;
 
-    device->SetClipboardText = BE_SetClipboardText;
-    device->GetClipboardText = BE_GetClipboardText;
-    device->HasClipboardText = BE_HasClipboardText;
+    device->SetClipboardText = HAIKU_SetClipboardText;
+    device->GetClipboardText = HAIKU_GetClipboardText;
+    device->HasClipboardText = HAIKU_HasClipboardText;
 
-    device->free = BE_DeleteDevice;
+    device->free = HAIKU_DeleteDevice;
 
     return device;
 }
 
 VideoBootStrap HAIKU_bootstrap = {
-	"haiku", "Haiku graphics",
-	BE_Available, BE_CreateDevice
+    "haiku", "Haiku graphics",
+    HAIKU_Available, HAIKU_CreateDevice
 };
 
-void BE_DeleteDevice(SDL_VideoDevice * device)
+void HAIKU_DeleteDevice(SDL_VideoDevice * device)
 {
-	SDL_free(device->driverdata);
-	SDL_free(device);
+    SDL_free(device->driverdata);
+    SDL_free(device);
 }
 
-int BE_VideoInit(_THIS)
+int HAIKU_VideoInit(_THIS)
 {
-	/* Initialize the Be Application for appserver interaction */
-	if (SDL_InitBeApp() < 0) {
-		return -1;
-	}
-	
-	/* Initialize video modes */
-	BE_InitModes(_this);
+    /* Initialize the Be Application for appserver interaction */
+    if (SDL_InitBeApp() < 0) {
+        return -1;
+    }
+    
+    /* Initialize video modes */
+    HAIKU_InitModes(_this);
 
-	/* Init the keymap */
-	BE_InitOSKeymap();
-	
-	
+    /* Init the keymap */
+    HAIKU_InitOSKeymap();
+
 #if SDL_VIDEO_OPENGL
         /* testgl application doesn't load library, just tries to load symbols */
         /* is it correct? if so we have to load library here */
-    BE_GL_LoadLibrary(_this, NULL);
+    HAIKU_GL_LoadLibrary(_this, NULL);
 #endif
 
-        /* We're done! */
+    /* We're done! */
     return (0);
 }
 
-int BE_Available(void)
+int HAIKU_Available(void)
 {
     return (1);
 }
 
-void BE_VideoQuit(_THIS)
+void HAIKU_VideoQuit(_THIS)
 {
 
-    BE_QuitModes(_this);
+    HAIKU_QuitModes(_this);
 
     SDL_QuitBeApp();
 }
diff --git a/source/src/video/haiku/SDL_bvideo.h b/source/src/video/haiku/SDL_bvideo.h
index 0e28220..a1d01fb 100644
--- a/source/src/video/haiku/SDL_bvideo.h
+++ b/source/src/video/haiku/SDL_bvideo.h
@@ -30,10 +30,10 @@
 #include "../SDL_sysvideo.h"
 
 
-extern void BE_VideoQuit(_THIS);
-extern int BE_VideoInit(_THIS);
-extern void BE_DeleteDevice(_THIS);
-extern int BE_Available(void);
+extern void HAIKU_VideoQuit(_THIS);
+extern int HAIKU_VideoInit(_THIS);
+extern void HAIKU_DeleteDevice(_THIS);
+extern int HAIKU_Available(void);
 
 #ifdef __cplusplus
 }
diff --git a/source/src/video/haiku/SDL_bwindow.cc b/source/src/video/haiku/SDL_bwindow.cc
index 0931abe..142a3fa 100644
--- a/source/src/video/haiku/SDL_bwindow.cc
+++ b/source/src/video/haiku/SDL_bwindow.cc
@@ -32,36 +32,36 @@
 #endif
 
 static SDL_INLINE SDL_BWin *_ToBeWin(SDL_Window *window) {
-	return ((SDL_BWin*)(window->driverdata));
+    return ((SDL_BWin*)(window->driverdata));
 }
 
 static SDL_INLINE SDL_BApp *_GetBeApp() {
-	return ((SDL_BApp*)be_app);
+    return ((SDL_BApp*)be_app);
 }
 
 static int _InitWindow(_THIS, SDL_Window *window) {
-	uint32 flags = 0;
-	window_look look = B_TITLED_WINDOW_LOOK;
+    uint32 flags = 0;
+    window_look look = B_TITLED_WINDOW_LOOK;
 
-	BRect bounds(
+    BRect bounds(
         window->x,
         window->y,
-        window->x + window->w - 1,	//BeWindows have an off-by-one px w/h thing
+        window->x + window->w - 1,    //BeWindows have an off-by-one px w/h thing
         window->y + window->h - 1
     );
     
     if(window->flags & SDL_WINDOW_FULLSCREEN) {
-    	/* TODO: Add support for this flag */
-    	printf(__FILE__": %d!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",__LINE__);
+        /* TODO: Add support for this flag */
+        printf(__FILE__": %d!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",__LINE__);
     }
     if(window->flags & SDL_WINDOW_OPENGL) {
-    	/* TODO: Add support for this flag */
+        /* TODO: Add support for this flag */
     }
     if(!(window->flags & SDL_WINDOW_RESIZABLE)) {
-    	flags |= B_NOT_RESIZABLE | B_NOT_ZOOMABLE;
+        flags |= B_NOT_RESIZABLE | B_NOT_ZOOMABLE;
     }
     if(window->flags & SDL_WINDOW_BORDERLESS) {
-    	look = B_NO_BORDER_WINDOW_LOOK;
+        look = B_NO_BORDER_WINDOW_LOOK;
     }
 
     SDL_BWin *bwin = new(std::nothrow) SDL_BWin(bounds, look, flags);
@@ -75,39 +75,39 @@
     return 0;
 }
 
-int BE_CreateWindow(_THIS, SDL_Window *window) {
+int HAIKU_CreateWindow(_THIS, SDL_Window *window) {
     if (_InitWindow(_this, window) < 0) {
         return -1;
     }
-	
-	/* Start window loop */
+    
+    /* Start window loop */
     _ToBeWin(window)->Show();
     return 0;
 }
 
-int BE_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) {
+int HAIKU_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) {
 
-	SDL_BWin *otherBWin = (SDL_BWin*)data;
-	if(!otherBWin->LockLooper())
-		return -1;
-	
-	/* Create the new window and initialize its members */
-	window->x = (int)otherBWin->Frame().left;
-	window->y = (int)otherBWin->Frame().top;
-	window->w = (int)otherBWin->Frame().Width();
-	window->h = (int)otherBWin->Frame().Height();
-	
-	/* Set SDL flags */
-	if(!(otherBWin->Flags() & B_NOT_RESIZABLE)) {
-		window->flags |= SDL_WINDOW_RESIZABLE;
-	}
-	
-	/* If we are out of memory, return the error code */
+    SDL_BWin *otherBWin = (SDL_BWin*)data;
+    if(!otherBWin->LockLooper())
+        return -1;
+    
+    /* Create the new window and initialize its members */
+    window->x = (int)otherBWin->Frame().left;
+    window->y = (int)otherBWin->Frame().top;
+    window->w = (int)otherBWin->Frame().Width();
+    window->h = (int)otherBWin->Frame().Height();
+    
+    /* Set SDL flags */
+    if(!(otherBWin->Flags() & B_NOT_RESIZABLE)) {
+        window->flags |= SDL_WINDOW_RESIZABLE;
+    }
+    
+    /* If we are out of memory, return the error code */
     if (_InitWindow(_this, window) < 0) {
         return -1;
     }
-	
-	/* TODO: Add any other SDL-supported window attributes here */
+    
+    /* TODO: Add any other SDL-supported window attributes here */
     _ToBeWin(window)->SetTitle(otherBWin->Title());
     
     /* Start window loop and unlock the other window */
@@ -117,107 +117,107 @@
     return 0;
 }
 
-void BE_SetWindowTitle(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_SET_TITLE);
-	msg.AddString("window-title", window->title);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_SetWindowTitle(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_SET_TITLE);
+    msg.AddString("window-title", window->title);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) {
-	/* FIXME: Icons not supported by Haiku */
+void HAIKU_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) {
+    /* FIXME: Icons not supported by Haiku */
 }
 
-void BE_SetWindowPosition(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_MOVE_WINDOW);
-	msg.AddInt32("window-x", window->x);
-	msg.AddInt32("window-y", window->y);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_SetWindowPosition(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_MOVE_WINDOW);
+    msg.AddInt32("window-x", window->x);
+    msg.AddInt32("window-y", window->y);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_SetWindowSize(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_RESIZE_WINDOW);
-	msg.AddInt32("window-w", window->w - 1);
-	msg.AddInt32("window-h", window->h - 1);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_SetWindowSize(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_RESIZE_WINDOW);
+    msg.AddInt32("window-w", window->w - 1);
+    msg.AddInt32("window-h", window->h - 1);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) {
-	BMessage msg(BWIN_SET_BORDERED);
-	msg.AddBool("window-border", bordered != SDL_FALSE);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) {
+    BMessage msg(BWIN_SET_BORDERED);
+    msg.AddBool("window-border", bordered != SDL_FALSE);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) {
-	BMessage msg(BWIN_SET_RESIZABLE);
-	msg.AddBool("window-resizable", resizable != SDL_FALSE);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) {
+    BMessage msg(BWIN_SET_RESIZABLE);
+    msg.AddBool("window-resizable", resizable != SDL_FALSE);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_ShowWindow(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_SHOW_WINDOW);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_ShowWindow(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_SHOW_WINDOW);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_HideWindow(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_HIDE_WINDOW);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_HideWindow(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_HIDE_WINDOW);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_RaiseWindow(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_SHOW_WINDOW);	/* Activate this window and move to front */
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_RaiseWindow(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_SHOW_WINDOW);    /* Activate this window and move to front */
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_MaximizeWindow(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_MAXIMIZE_WINDOW);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_MaximizeWindow(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_MAXIMIZE_WINDOW);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_MinimizeWindow(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_MINIMIZE_WINDOW);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_MinimizeWindow(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_MINIMIZE_WINDOW);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_RestoreWindow(_THIS, SDL_Window * window) {
-	BMessage msg(BWIN_RESTORE_WINDOW);
-	_ToBeWin(window)->PostMessage(&msg);
+void HAIKU_RestoreWindow(_THIS, SDL_Window * window) {
+    BMessage msg(BWIN_RESTORE_WINDOW);
+    _ToBeWin(window)->PostMessage(&msg);
 }
 
-void BE_SetWindowFullscreen(_THIS, SDL_Window * window,
-		SDL_VideoDisplay * display, SDL_bool fullscreen) {
-	/* Haiku tracks all video display information */
-	BMessage msg(BWIN_FULLSCREEN);
-	msg.AddBool("fullscreen", fullscreen);
-	_ToBeWin(window)->PostMessage(&msg);
-	
+void HAIKU_SetWindowFullscreen(_THIS, SDL_Window * window,
+        SDL_VideoDisplay * display, SDL_bool fullscreen) {
+    /* Haiku tracks all video display information */
+    BMessage msg(BWIN_FULLSCREEN);
+    msg.AddBool("fullscreen", fullscreen);
+    _ToBeWin(window)->PostMessage(&msg);
+    
 }
 
-int BE_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) {
-	/* FIXME: Not Haiku supported */
-	return -1;
+int HAIKU_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) {
+    /* FIXME: Not Haiku supported */
+    return -1;
 }
 
-int BE_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) {
-	/* FIXME: Not Haiku supported */
-	return -1;
+int HAIKU_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp) {
+    /* FIXME: Not Haiku supported */
+    return -1;
 }
 
 
-void BE_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) {
-	/* TODO: Implement this! */
+void HAIKU_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) {
+    /* TODO: Implement this! */
 }
 
-void BE_DestroyWindow(_THIS, SDL_Window * window) {
-	_ToBeWin(window)->LockLooper();	/* This MUST be locked */
-	_GetBeApp()->ClearID(_ToBeWin(window));
-	_ToBeWin(window)->Quit();
-	window->driverdata = NULL;
+void HAIKU_DestroyWindow(_THIS, SDL_Window * window) {
+    _ToBeWin(window)->LockLooper();    /* This MUST be locked */
+    _GetBeApp()->ClearID(_ToBeWin(window));
+    _ToBeWin(window)->Quit();
+    window->driverdata = NULL;
 }
 
-SDL_bool BE_GetWindowWMInfo(_THIS, SDL_Window * window,
+SDL_bool HAIKU_GetWindowWMInfo(_THIS, SDL_Window * window,
                                     struct SDL_SysWMinfo *info) {
-	/* FIXME: What is the point of this? What information should be included? */
-	return SDL_FALSE;
+    /* FIXME: What is the point of this? What information should be included? */
+    return SDL_FALSE;
 }
 
 
diff --git a/source/src/video/haiku/SDL_bwindow.h b/source/src/video/haiku/SDL_bwindow.h
index 100ffed..2894f27 100644
--- a/source/src/video/haiku/SDL_bwindow.h
+++ b/source/src/video/haiku/SDL_bwindow.h
@@ -26,26 +26,26 @@
 #include "../SDL_sysvideo.h"
 
 
-extern int BE_CreateWindow(_THIS, SDL_Window *window);
-extern int BE_CreateWindowFrom(_THIS, SDL_Window * window, const void *data);
-extern void BE_SetWindowTitle(_THIS, SDL_Window * window);
-extern void BE_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon);
-extern void BE_SetWindowPosition(_THIS, SDL_Window * window);
-extern void BE_SetWindowSize(_THIS, SDL_Window * window);
-extern void BE_ShowWindow(_THIS, SDL_Window * window);
-extern void BE_HideWindow(_THIS, SDL_Window * window);
-extern void BE_RaiseWindow(_THIS, SDL_Window * window);
-extern void BE_MaximizeWindow(_THIS, SDL_Window * window);
-extern void BE_MinimizeWindow(_THIS, SDL_Window * window);
-extern void BE_RestoreWindow(_THIS, SDL_Window * window);
-extern void BE_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered);
-extern void BE_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable);
-extern void BE_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
-extern int BE_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp);
-extern int BE_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
-extern void BE_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
-extern void BE_DestroyWindow(_THIS, SDL_Window * window);
-extern SDL_bool BE_GetWindowWMInfo(_THIS, SDL_Window * window,
+extern int HAIKU_CreateWindow(_THIS, SDL_Window *window);
+extern int HAIKU_CreateWindowFrom(_THIS, SDL_Window * window, const void *data);
+extern void HAIKU_SetWindowTitle(_THIS, SDL_Window * window);
+extern void HAIKU_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon);
+extern void HAIKU_SetWindowPosition(_THIS, SDL_Window * window);
+extern void HAIKU_SetWindowSize(_THIS, SDL_Window * window);
+extern void HAIKU_ShowWindow(_THIS, SDL_Window * window);
+extern void HAIKU_HideWindow(_THIS, SDL_Window * window);
+extern void HAIKU_RaiseWindow(_THIS, SDL_Window * window);
+extern void HAIKU_MaximizeWindow(_THIS, SDL_Window * window);
+extern void HAIKU_MinimizeWindow(_THIS, SDL_Window * window);
+extern void HAIKU_RestoreWindow(_THIS, SDL_Window * window);
+extern void HAIKU_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered);
+extern void HAIKU_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable);
+extern void HAIKU_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
+extern int HAIKU_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp);
+extern int HAIKU_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
+extern void HAIKU_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
+extern void HAIKU_DestroyWindow(_THIS, SDL_Window * window);
+extern SDL_bool HAIKU_GetWindowWMInfo(_THIS, SDL_Window * window,
                                     struct SDL_SysWMinfo *info);
 
 
diff --git a/source/src/video/kmsdrm/SDL_kmsdrmmouse.c b/source/src/video/kmsdrm/SDL_kmsdrmmouse.c
index e23dd13..0474089 100644
--- a/source/src/video/kmsdrm/SDL_kmsdrmmouse.c
+++ b/source/src/video/kmsdrm/SDL_kmsdrmmouse.c
@@ -487,12 +487,12 @@
        That's why we move the cursor graphic ONLY. */
     if (mouse != NULL && mouse->cur_cursor != NULL && mouse->cur_cursor->driverdata != NULL) {
         curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
-	drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
-	ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
+        drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
+        ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
 
-	if (ret) {
-	    SDL_SetError("drmModeMoveCursor() failed.");
-	}
+        if (ret) {
+            SDL_SetError("drmModeMoveCursor() failed.");
+        }
     }
 }
 
diff --git a/source/src/video/kmsdrm/SDL_kmsdrmopengles.c b/source/src/video/kmsdrm/SDL_kmsdrmopengles.c
index 7a0b079..fc6304d 100644
--- a/source/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/source/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -50,23 +50,23 @@
     KMSDRM_FBInfo *fb_info;
 
     if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, wdata->egl_surface))) {
-	SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed on CRTC setup");
-	return SDL_FALSE;
+        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed on CRTC setup");
+        return SDL_FALSE;
     }
 
-    wdata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(wdata->gs);
-    if (wdata->next_bo == NULL) {
-	SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer on CRTC setup");
-	return SDL_FALSE;
+    wdata->crtc_bo = KMSDRM_gbm_surface_lock_front_buffer(wdata->gs);
+    if (wdata->crtc_bo == NULL) {
+        SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer on CRTC setup");
+        return SDL_FALSE;
     }
 
-    fb_info = KMSDRM_FBFromBO(_this, wdata->next_bo);
+    fb_info = KMSDRM_FBFromBO(_this, wdata->crtc_bo);
     if (fb_info == NULL) {
-	return SDL_FALSE;
+        return SDL_FALSE;
     }
 
     if(KMSDRM_drmModeSetCrtc(vdata->drm_fd, displaydata->crtc_id, fb_info->fb_id,
-			    0, 0, &vdata->saved_conn_id, 1, &displaydata->cur_mode) != 0) {
+                            0, 0, &vdata->saved_conn_id, 1, &displaydata->cur_mode) != 0) {
        SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not set up CRTC to a GBM buffer");
        return SDL_FALSE;
 
@@ -153,14 +153,14 @@
     } else {
         /* Queue page flip at vsync */
 
-	/* Have we already setup the CRTC to one of the GBM buffers? Do so if we have not,
+        /* Have we already setup the CRTC to one of the GBM buffers? Do so if we have not,
            or FlipPage won't work in some cases. */
-	if (!wdata->crtc_ready) {
+        if (!wdata->crtc_ready) {
             if(!KMSDRM_GLES_SetupCrtc(_this, window)) {
                 SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not set up CRTC for doing vsync-ed pageflips");
                 return 0;
             }
-	}
+        }
 
         /* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "drmModePageFlip(%d, %u, %u, DRM_MODE_PAGE_FLIP_EVENT, &wdata->waiting_for_flip)",
             vdata->drm_fd, displaydata->crtc_id, fb_info->fb_id); */
diff --git a/source/src/video/kmsdrm/SDL_kmsdrmvideo.c b/source/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 7855eed..bacbe0c 100644
--- a/source/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/source/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -41,20 +41,27 @@
 #include "SDL_kmsdrmopengles.h"
 #include "SDL_kmsdrmmouse.h"
 #include "SDL_kmsdrmdyn.h"
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
 
-#define KMSDRM_DRI_CARD_0 "/dev/dri/card0"
+#define KMSDRM_DRI_PATH "/dev/dri/"
 
 static int
-KMSDRM_Available(void)
+check_modestting(int devindex)
 {
-    int available = 0;
+    SDL_bool available = SDL_FALSE;
+    char device[512];
+    int drm_fd;
 
-    int drm_fd = open(KMSDRM_DRI_CARD_0, O_RDWR | O_CLOEXEC);
+    SDL_snprintf(device, sizeof (device), "%scard%d", KMSDRM_DRI_PATH, devindex);
+
+    drm_fd = open(device, O_RDWR | O_CLOEXEC);
     if (drm_fd >= 0) {
         if (SDL_KMSDRM_LoadSymbols()) {
             drmModeRes *resources = KMSDRM_drmModeGetResources(drm_fd);
             if (resources != NULL) {
-                available = 1;
+                available = SDL_TRUE;
                 KMSDRM_drmModeFreeResources(resources);
             }
             SDL_KMSDRM_UnloadSymbols();
@@ -63,6 +70,66 @@
     }
 
     return available;
+}
+
+static int get_dricount(void)
+{
+    int devcount = 0;
+    struct dirent *res;
+    struct stat sb;
+    DIR *folder;
+
+    if (!(stat(KMSDRM_DRI_PATH, &sb) == 0
+                && S_ISDIR(sb.st_mode))) {
+        printf("The path %s cannot be opened or is not available\n",
+               KMSDRM_DRI_PATH);
+        return 0;
+    }
+
+    if (access(KMSDRM_DRI_PATH, F_OK) == -1) {
+        printf("The path %s cannot be opened\n",
+               KMSDRM_DRI_PATH);
+        return 0;
+    }
+
+    folder = opendir(KMSDRM_DRI_PATH);
+    if (folder) {
+        while ((res = readdir(folder))) {
+            if (res->d_type == DT_CHR) {
+                devcount++;
+            }
+        }
+        closedir(folder);
+    }
+
+    return devcount;
+}
+
+static int
+get_driindex(void)
+{
+    const int devcount = get_dricount();
+    int i;
+
+    for (i = 0; i < devcount; i++) {
+        if (check_modestting(i)) {
+            return i;
+        }
+    }
+
+    return -ENOENT;
+}
+
+static int
+KMSDRM_Available(void)
+{
+    int ret = -ENOENT;
+
+    ret = get_driindex();
+    if (ret >= 0)
+        return 1;
+
+    return ret;
 }
 
 static void
@@ -83,7 +150,11 @@
     SDL_VideoDevice *device;
     SDL_VideoData *vdata;
 
-    if (devindex < 0 || devindex > 99) {
+    if (!devindex || (devindex > 99)) {
+        devindex = get_driindex();
+    }
+
+    if (devindex < 0) {
         SDL_SetError("devindex (%d) must be between 0 and 99.\n", devindex);
         return NULL;
     }
@@ -566,6 +637,10 @@
     if(data) {
         /* Wait for any pending page flips and unlock buffer */
         KMSDRM_WaitPageFlip(_this, data, -1);
+        if (data->crtc_bo != NULL) {
+            KMSDRM_gbm_surface_release_buffer(data->gs, data->crtc_bo);
+            data->crtc_bo = NULL;
+        }
         if (data->next_bo != NULL) {
             KMSDRM_gbm_surface_release_buffer(data->gs, data->next_bo);
             data->next_bo = NULL;
diff --git a/source/src/video/kmsdrm/SDL_kmsdrmvideo.h b/source/src/video/kmsdrm/SDL_kmsdrmvideo.h
index 5f00f0e..34f0b10 100644
--- a/source/src/video/kmsdrm/SDL_kmsdrmvideo.h
+++ b/source/src/video/kmsdrm/SDL_kmsdrmvideo.h
@@ -62,6 +62,7 @@
     struct gbm_surface *gs;
     struct gbm_bo *current_bo;
     struct gbm_bo *next_bo;
+    struct gbm_bo *crtc_bo;
     SDL_bool waiting_for_flip;
     SDL_bool crtc_ready;
     SDL_bool double_buffer;
diff --git a/source/src/video/raspberry/SDL_rpivideo.c b/source/src/video/raspberry/SDL_rpivideo.c
index e386380..c4f4a60 100644
--- a/source/src/video/raspberry/SDL_rpivideo.c
+++ b/source/src/video/raspberry/SDL_rpivideo.c
@@ -343,17 +343,17 @@
     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
 
     if(data) {
-	if (data->double_buffer) {
-	    /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */
-	    SDL_LockMutex(data->vsync_cond_mutex);
-	    SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex);
-	    SDL_UnlockMutex(data->vsync_cond_mutex);
+        if (data->double_buffer) {
+            /* Wait for vsync, and then stop vsync callbacks and destroy related stuff, if needed */
+            SDL_LockMutex(data->vsync_cond_mutex);
+            SDL_CondWait(data->vsync_cond, data->vsync_cond_mutex);
+            SDL_UnlockMutex(data->vsync_cond_mutex);
 
-	    vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
+            vc_dispmanx_vsync_callback(displaydata->dispman_display, NULL, NULL);
 
-	    SDL_DestroyCond(data->vsync_cond);
-	    SDL_DestroyMutex(data->vsync_cond_mutex);
-	}
+            SDL_DestroyCond(data->vsync_cond);
+            SDL_DestroyMutex(data->vsync_cond_mutex);
+        }
 
 #if SDL_VIDEO_OPENGL_EGL
         if (data->egl_surface != EGL_NO_SURFACE) {
diff --git a/source/src/video/uikit/SDL_uikitappdelegate.m b/source/src/video/uikit/SDL_uikitappdelegate.m
index a942dfd..15762d2 100644
--- a/source/src/video/uikit/SDL_uikitappdelegate.m
+++ b/source/src/video/uikit/SDL_uikitappdelegate.m
@@ -445,30 +445,7 @@
 #if !TARGET_OS_TV
 - (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation
 {
-    BOOL isLandscape = UIInterfaceOrientationIsLandscape(application.statusBarOrientation);
-    SDL_VideoDevice *_this = SDL_GetVideoDevice();
-
-    if (_this && _this->num_displays > 0) {
-        SDL_DisplayMode *desktopmode = &_this->displays[0].desktop_mode;
-        SDL_DisplayMode *currentmode = &_this->displays[0].current_mode;
-
-        /* The desktop display mode should be kept in sync with the screen
-         * orientation so that updating a window's fullscreen state to
-         * SDL_WINDOW_FULLSCREEN_DESKTOP keeps the window dimensions in the
-         * correct orientation. */
-        if (isLandscape != (desktopmode->w > desktopmode->h)) {
-            int height = desktopmode->w;
-            desktopmode->w = desktopmode->h;
-            desktopmode->h = height;
-        }
-
-        /* Same deal with the current mode + SDL_GetCurrentDisplayMode. */
-        if (isLandscape != (currentmode->w > currentmode->h)) {
-            int height = currentmode->w;
-            currentmode->w = currentmode->h;
-            currentmode->h = height;
-        }
-    }
+    SDL_OnApplicationDidChangeStatusBarOrientation();
 }
 #endif
 
diff --git a/source/src/video/uikit/SDL_uikitmessagebox.m b/source/src/video/uikit/SDL_uikitmessagebox.m
index 7950c8e..cf2a8f3 100644
--- a/source/src/video/uikit/SDL_uikitmessagebox.m
+++ b/source/src/video/uikit/SDL_uikitmessagebox.m
@@ -109,6 +109,7 @@
         alertwindow.hidden = YES;
     }
 
+#if !TARGET_OS_TV
     /* Force the main SDL window to re-evaluate home indicator state */
     SDL_Window *focus = SDL_GetFocusWindow();
     if (focus) {
@@ -120,6 +121,7 @@
             }
         }
     }
+#endif /* !TARGET_OS_TV */
 
     *buttonid = messageboxdata->buttons[clickedindex].buttonid;
     return YES;
diff --git a/source/src/video/uikit/SDL_uikitmetalview.m b/source/src/video/uikit/SDL_uikitmetalview.m
index 104189d..436e742 100644
--- a/source/src/video/uikit/SDL_uikitmetalview.m
+++ b/source/src/video/uikit/SDL_uikitmetalview.m
@@ -49,9 +49,8 @@
 {
     if ((self = [super initWithFrame:frame])) {
         self.tag = METALVIEW_TAG;
-        /* Set the desired scale. The default drawableSize of a CAMetalLayer
-         * is its bounds x its scale so nothing further needs to be done. */
         self.layer.contentsScale = scale;
+        [self updateDrawableSize];
     }
 
     return self;
@@ -61,6 +60,15 @@
 - (void)layoutSubviews
 {
     [super layoutSubviews];
+    [self updateDrawableSize];
+}
+
+- (void)updateDrawableSize
+{
+    CGSize size = self.bounds.size;
+    size.width *= self.layer.contentsScale;
+    size.height *= self.layer.contentsScale;
+    ((CAMetalLayer *)self.layer).drawableSize = size;
 }
 
 @end
@@ -72,9 +80,9 @@
     SDL_uikitview *view = (SDL_uikitview*)data.uiwindow.rootViewController.view;
     CGFloat scale = 1.0;
 
-	if ([view isKindOfClass:[SDL_uikitmetalview class]]) {
-		return (SDL_uikitmetalview *)view;
-	}
+    if ([view isKindOfClass:[SDL_uikitmetalview class]]) {
+        return (SDL_uikitmetalview *)view;
+    }
 
     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
         /* Set the scale to the natural scale factor of the screen - then
diff --git a/source/src/video/uikit/SDL_uikitmodes.h b/source/src/video/uikit/SDL_uikitmodes.h
index a1df0d4..b5c0c65 100644
--- a/source/src/video/uikit/SDL_uikitmodes.h
+++ b/source/src/video/uikit/SDL_uikitmodes.h
@@ -45,6 +45,10 @@
 extern void UIKit_QuitModes(_THIS);
 extern int UIKit_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);
 
+#if !TARGET_OS_TV
+extern void SDL_OnApplicationDidChangeStatusBarOrientation(void);
+#endif
+
 #endif /* SDL_uikitmodes_h_ */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/uikit/SDL_uikitmodes.m b/source/src/video/uikit/SDL_uikitmodes.m
index 75e256b..7ddf107 100644
--- a/source/src/video/uikit/SDL_uikitmodes.m
+++ b/source/src/video/uikit/SDL_uikitmodes.m
@@ -25,6 +25,8 @@
 #include "SDL_assert.h"
 #include "SDL_uikitmodes.h"
 
+#include "../../events/SDL_events_c.h"
+
 @implementation SDL_DisplayData
 
 @synthesize uiscreen;
@@ -188,6 +190,9 @@
                 return -1;
             }
         }
+#if !TARGET_OS_TV
+        SDL_OnApplicationDidChangeStatusBarOrientation();
+#endif
     }
 
     return 0;
@@ -319,6 +324,57 @@
     }
 }
 
+#if !TARGET_OS_TV
+void SDL_OnApplicationDidChangeStatusBarOrientation()
+{
+    BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
+    SDL_VideoDisplay *display = SDL_GetDisplay(0);
+
+    if (display) {
+        SDL_DisplayMode *desktopmode = &display->desktop_mode;
+        SDL_DisplayMode *currentmode = &display->current_mode;
+        SDL_DisplayOrientation orientation = SDL_ORIENTATION_UNKNOWN;
+
+        /* The desktop display mode should be kept in sync with the screen
+         * orientation so that updating a window's fullscreen state to
+         * SDL_WINDOW_FULLSCREEN_DESKTOP keeps the window dimensions in the
+         * correct orientation. */
+        if (isLandscape != (desktopmode->w > desktopmode->h)) {
+            int height = desktopmode->w;
+            desktopmode->w = desktopmode->h;
+            desktopmode->h = height;
+        }
+
+        /* Same deal with the current mode + SDL_GetCurrentDisplayMode. */
+        if (isLandscape != (currentmode->w > currentmode->h)) {
+            int height = currentmode->w;
+            currentmode->w = currentmode->h;
+            currentmode->h = height;
+        }
+
+        switch ([UIApplication sharedApplication].statusBarOrientation) {
+        case UIInterfaceOrientationPortrait:
+            orientation = SDL_ORIENTATION_PORTRAIT;
+            break;
+        case UIInterfaceOrientationPortraitUpsideDown:
+            orientation = SDL_ORIENTATION_PORTRAIT_FLIPPED;
+            break;
+        case UIInterfaceOrientationLandscapeLeft:
+            /* Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 */
+            orientation = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
+            break;
+        case UIInterfaceOrientationLandscapeRight:
+            /* Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 */
+            orientation = SDL_ORIENTATION_LANDSCAPE;
+            break;
+        default:
+            break;
+        }
+        SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
+    }
+}
+#endif /* !TARGET_OS_TV */
+
 #endif /* SDL_VIDEO_DRIVER_UIKIT */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/uikit/SDL_uikitopenglview.m b/source/src/video/uikit/SDL_uikitopenglview.m
index b0628bf..9024376 100644
--- a/source/src/video/uikit/SDL_uikitopenglview.m
+++ b/source/src/video/uikit/SDL_uikitopenglview.m
@@ -101,7 +101,7 @@
                 SDL_SetError("sRGB drawables are not supported.");
                 return nil;
             }
-        } else if (rBits >= 8 || gBits >= 8 || bBits >= 8) {
+        } else if (rBits >= 8 || gBits >= 8 || bBits >= 8 || aBits > 0) {
             /* if user specifically requests rbg888 or some color format higher than 16bpp */
             colorFormat = kEAGLColorFormatRGBA8;
             colorBufferFormat = GL_RGBA8;
diff --git a/source/src/video/uikit/SDL_uikitvideo.m b/source/src/video/uikit/SDL_uikitvideo.m
index e74339f..10d1dde 100644
--- a/source/src/video/uikit/SDL_uikitvideo.m
+++ b/source/src/video/uikit/SDL_uikitvideo.m
@@ -233,6 +233,17 @@
     NSLog(@"%s", text);
 }
 
+/*
+ * iOS Tablet detection
+ *
+ * This doesn't really have aything to do with the interfaces of the SDL video
+ * subsystem, but we need to stuff this into an Objective-C source code file.
+ */
+SDL_bool SDL_IsIPad(void)
+{
+    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
+}
+
 #endif /* SDL_VIDEO_DRIVER_UIKIT */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/uikit/SDL_uikitview.m b/source/src/video/uikit/SDL_uikitview.m
index bd60c55..caabfac 100644
--- a/source/src/video/uikit/SDL_uikitview.m
+++ b/source/src/video/uikit/SDL_uikitview.m
@@ -252,34 +252,34 @@
 
 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
 {
-	if (!SDL_AppleTVRemoteOpenedAsJoystick) {
-    	for (UIPress *press in presses) {
-        	SDL_Scancode scancode = [self scancodeFromPressType:press.type];
-        	SDL_SendKeyboardKey(SDL_PRESSED, scancode);
-    	}
-	}
+    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
+        for (UIPress *press in presses) {
+            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
+            SDL_SendKeyboardKey(SDL_PRESSED, scancode);
+        }
+    }
     [super pressesBegan:presses withEvent:event];
 }
 
 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
 {
-	if (!SDL_AppleTVRemoteOpenedAsJoystick) {
-		for (UIPress *press in presses) {
-			SDL_Scancode scancode = [self scancodeFromPressType:press.type];
-			SDL_SendKeyboardKey(SDL_RELEASED, scancode);
-		}
-	}
+    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
+        for (UIPress *press in presses) {
+            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
+            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+        }
+    }
     [super pressesEnded:presses withEvent:event];
 }
 
 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
 {
-	if (!SDL_AppleTVRemoteOpenedAsJoystick) {
-		for (UIPress *press in presses) {
-			SDL_Scancode scancode = [self scancodeFromPressType:press.type];
-			SDL_SendKeyboardKey(SDL_RELEASED, scancode);
-		}
-	}
+    if (!SDL_AppleTVRemoteOpenedAsJoystick) {
+        for (UIPress *press in presses) {
+            SDL_Scancode scancode = [self scancodeFromPressType:press.type];
+            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+        }
+    }
     [super pressesCancelled:presses withEvent:event];
 }
 
diff --git a/source/src/video/uikit/SDL_uikitviewcontroller.m b/source/src/video/uikit/SDL_uikitviewcontroller.m
index 2962742..49a39b6 100644
--- a/source/src/video/uikit/SDL_uikitviewcontroller.m
+++ b/source/src/video/uikit/SDL_uikitviewcontroller.m
@@ -74,6 +74,8 @@
 #if SDL_IPHONE_KEYBOARD
     UITextField *textField;
     BOOL rotatingOrientation;
+    NSString *changeText;
+    NSString *obligateForBackspace;
 #endif
 }
 
@@ -250,10 +252,12 @@
 /* Set ourselves up as a UITextFieldDelegate */
 - (void)initKeyboard
 {
+    changeText = nil;
+    obligateForBackspace = @"                                                                "; /* 64 space */
     textField = [[UITextField alloc] initWithFrame:CGRectZero];
     textField.delegate = self;
     /* placeholder so there is something to delete! */
-    textField.text = @" ";
+    textField.text = obligateForBackspace;
 
     /* set UITextInputTrait properties, mostly to defaults */
     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
@@ -267,11 +271,12 @@
     textField.hidden = YES;
     keyboardVisible = NO;
 
-#if !TARGET_OS_TV
     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+#if !TARGET_OS_TV
     [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
     [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
 #endif
+    [center addObserver:self selector:@selector(textFieldTextDidChange:) name:UITextFieldTextDidChangeNotification object:nil];
 }
 
 - (void)setView:(UIView *)view
@@ -310,11 +315,12 @@
 
 - (void)deinitKeyboard
 {
-#if !TARGET_OS_TV
     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+#if !TARGET_OS_TV
     [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
     [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
 #endif
+    [center removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
 }
 
 /* reveal onscreen virtual keyboard */
@@ -352,6 +358,50 @@
         SDL_StopTextInput();
     }
     [self setKeyboardHeight:0];
+}
+
+- (void)textFieldTextDidChange:(NSNotification *)notification
+{
+    if (changeText!=nil && textField.markedTextRange == nil)
+    {
+        NSUInteger len = changeText.length;
+        if (len > 0) {
+            /* Go through all the characters in the string we've been sent and
+             * convert them to key presses */
+            int i;
+            for (i = 0; i < len; i++) {
+                unichar c = [changeText characterAtIndex:i];
+                SDL_Scancode code;
+                Uint16 mod;
+
+                if (c < 127) {
+                    /* Figure out the SDL_Scancode and SDL_keymod for this unichar */
+                    code = unicharToUIKeyInfoTable[c].code;
+                    mod  = unicharToUIKeyInfoTable[c].mod;
+                } else {
+                    /* We only deal with ASCII right now */
+                    code = SDL_SCANCODE_UNKNOWN;
+                    mod = 0;
+                }
+
+                if (mod & KMOD_SHIFT) {
+                    /* If character uses shift, press shift down */
+                    SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
+                }
+
+                /* send a keydown and keyup even for the character */
+                SDL_SendKeyboardKey(SDL_PRESSED, code);
+                SDL_SendKeyboardKey(SDL_RELEASED, code);
+
+                if (mod & KMOD_SHIFT) {
+                    /* If character uses shift, press shift back up */
+                    SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
+                }
+            }
+            SDL_SendKeyboardText([changeText UTF8String]);
+        }
+        changeText = nil;
+    }
 }
 
 - (void)updateKeyboard
@@ -392,49 +442,20 @@
 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
 {
     NSUInteger len = string.length;
-
     if (len == 0) {
-        /* it wants to replace text with nothing, ie a delete */
-        SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
-        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
-    } else {
-        /* go through all the characters in the string we've been sent and
-         * convert them to key presses */
-        int i;
-        for (i = 0; i < len; i++) {
-            unichar c = [string characterAtIndex:i];
-            Uint16 mod = 0;
-            SDL_Scancode code;
-
-            if (c < 127) {
-                /* figure out the SDL_Scancode and SDL_keymod for this unichar */
-                code = unicharToUIKeyInfoTable[c].code;
-                mod  = unicharToUIKeyInfoTable[c].mod;
-            } else {
-                /* we only deal with ASCII right now */
-                code = SDL_SCANCODE_UNKNOWN;
-                mod = 0;
-            }
-
-            if (mod & KMOD_SHIFT) {
-                /* If character uses shift, press shift down */
-                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
-            }
-
-            /* send a keydown and keyup even for the character */
-            SDL_SendKeyboardKey(SDL_PRESSED, code);
-            SDL_SendKeyboardKey(SDL_RELEASED, code);
-
-            if (mod & KMOD_SHIFT) {
-                /* If character uses shift, press shift back up */
-                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
-            }
+        changeText = nil;
+        if (textField.markedTextRange == nil) {
+            /* it wants to replace text with nothing, ie a delete */
+            SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
+            SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
         }
-
-        SDL_SendKeyboardText([string UTF8String]);
+        if (textField.text.length < 16) {
+            textField.text = obligateForBackspace;
+        }
+    } else {
+        changeText = string;
     }
-
-    return NO; /* don't allow the edit! (keep placeholder text there) */
+    return YES;
 }
 
 /* Terminates the editing session */
@@ -498,7 +519,7 @@
     @autoreleasepool {
         SDL_uikitviewcontroller *vc = GetWindowViewController(window);
         if (vc != nil) {
-            return vc.isKeyboardVisible;
+            return vc.keyboardVisible;
         }
         return SDL_FALSE;
     }
diff --git a/source/src/video/wayland/SDL_waylandevents.c b/source/src/video/wayland/SDL_waylandevents.c
index 53453a2..0c953a5 100644
--- a/source/src/video/wayland/SDL_waylandevents.c
+++ b/source/src/video/wayland/SDL_waylandevents.c
@@ -40,6 +40,7 @@
 
 #include "pointer-constraints-unstable-v1-client-protocol.h"
 #include "relative-pointer-unstable-v1-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
 #include "xdg-shell-unstable-v6-client-protocol.h"
 
 #include <linux/input.h>
@@ -177,6 +178,8 @@
 {
     SDL_VideoData *d = _this->driverdata;
 
+    WAYLAND_wl_display_flush(d->display);
+
     if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, 0)) {
         WAYLAND_wl_display_dispatch(d->display);
     }
@@ -263,7 +266,9 @@
 
         switch (rc) {
             case SDL_HITTEST_DRAGGABLE:
-                if (input->display->shell.zxdg) {
+                if (input->display->shell.xdg) {
+                    xdg_toplevel_move(window_data->shell_surface.xdg.roleobj.toplevel, input->seat, serial);
+                } else if (input->display->shell.zxdg) {
                     zxdg_toplevel_v6_move(window_data->shell_surface.zxdg.roleobj.toplevel, input->seat, serial);
                 } else {
                     wl_shell_surface_move(window_data->shell_surface.wl, input->seat, serial);
@@ -278,7 +283,9 @@
             case SDL_HITTEST_RESIZE_BOTTOM:
             case SDL_HITTEST_RESIZE_BOTTOMLEFT:
             case SDL_HITTEST_RESIZE_LEFT:
-                if (input->display->shell.zxdg) {
+                if (input->display->shell.xdg) {
+                    xdg_toplevel_resize(window_data->shell_surface.xdg.roleobj.toplevel, input->seat, serial, directions_zxdg[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
+                } else if (input->display->shell.zxdg) {
                     zxdg_toplevel_v6_resize(window_data->shell_surface.zxdg.roleobj.toplevel, input->seat, serial, directions_zxdg[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
                 } else {
                     wl_shell_surface_resize(window_data->shell_surface.wl, input->seat, serial, directions_wl[rc - SDL_HITTEST_RESIZE_TOPLEFT]);
@@ -742,7 +749,7 @@
 
 static void
 data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device,
-			                  struct wl_data_offer *id)
+                              struct wl_data_offer *id)
 {
     SDL_WaylandDataOffer *data_offer = NULL;
 
@@ -760,7 +767,7 @@
 
 static void
 data_device_handle_enter(void *data, struct wl_data_device *wl_data_device,
-		                 uint32_t serial, struct wl_surface *surface,
+                         uint32_t serial, struct wl_surface *surface,
                          wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id)
 {
     SDL_WaylandDataDevice *data_device = data;
@@ -803,7 +810,7 @@
 
 static void
 data_device_handle_motion(void *data, struct wl_data_device *wl_data_device,
-		                  uint32_t time, wl_fixed_t x, wl_fixed_t y)
+                          uint32_t time, wl_fixed_t x, wl_fixed_t y)
 {
 }
 
@@ -842,7 +849,7 @@
 
 static void
 data_device_handle_selection(void *data, struct wl_data_device *wl_data_device,
-			                 struct wl_data_offer *id)
+                             struct wl_data_offer *id)
 {    
     SDL_WaylandDataDevice *data_device = data;
     SDL_WaylandDataOffer *offer = NULL;
diff --git a/source/src/video/wayland/SDL_waylandtouch.c b/source/src/video/wayland/SDL_waylandtouch.c
index 005b47f..1cf37c1 100644
--- a/source/src/video/wayland/SDL_waylandtouch.c
+++ b/source/src/video/wayland/SDL_waylandtouch.c
@@ -89,9 +89,9 @@
     */
 
     SDL_TouchID deviceId = 1;
-	if (SDL_AddTouch(deviceId, "qt_touch_extension") < 0) {
-		 SDL_Log("error: can't add touch %s, %d", __FILE__, __LINE__);
-	}
+    if (SDL_AddTouch(deviceId, "qt_touch_extension") < 0) {
+         SDL_Log("error: can't add touch %s, %d", __FILE__, __LINE__);
+    }
 
     switch (touchState) {
         case QtWaylandTouchPointPressed:
diff --git a/source/src/video/wayland/SDL_waylandtouch.h b/source/src/video/wayland/SDL_waylandtouch.h
index 9efc5a5..eba0da8 100644
--- a/source/src/video/wayland/SDL_waylandtouch.h
+++ b/source/src/video/wayland/SDL_waylandtouch.h
@@ -19,12 +19,12 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_waylandtouch_h_
+#define SDL_waylandtouch_h_
+
 #include "../../SDL_internal.h"
 
 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
-
-#ifndef SDL_waylandtouch_h_
-#define SDL_waylandtouch_h_
 
 #include "SDL_waylandvideo.h"
 #include <stdint.h>
@@ -347,6 +347,6 @@
              QT_WINDOWMANAGER_OPEN_URL, remaining, url);
 }
 
-#endif /* SDL_waylandtouch_h_ */
-
 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
+
+#endif /* SDL_waylandtouch_h_ */
diff --git a/source/src/video/wayland/SDL_waylandvideo.c b/source/src/video/wayland/SDL_waylandvideo.c
index 8401a08..b6155e7 100644
--- a/source/src/video/wayland/SDL_waylandvideo.c
+++ b/source/src/video/wayland/SDL_waylandvideo.c
@@ -45,6 +45,7 @@
 #include "SDL_waylanddyn.h"
 #include <wayland-util.h>
 
+#include "xdg-shell-client-protocol.h"
 #include "xdg-shell-unstable-v6-client-protocol.h"
 
 #define WAYLANDVID_DRIVER_NAME "wayland"
@@ -67,10 +68,10 @@
 get_classname()
 {
 /* !!! FIXME: this is probably wrong, albeit harmless in many common cases. From protocol spec:
-	"The surface class identifies the general class of applications
-	to which the surface belongs. A common convention is to use the
-	file name (or the full path if it is a non-standard location) of
-	the application's .desktop file as the class." */
+    "The surface class identifies the general class of applications
+    to which the surface belongs. A common convention is to use the
+    file name (or the full path if it is a non-standard location) of
+    the application's .desktop file as the class." */
 
     char *spot;
 #if defined(__LINUX__) || defined(__FREEBSD__)
@@ -328,6 +329,17 @@
 
 
 static void
+handle_ping_xdg_wm_base(void *data, struct xdg_wm_base *xdg, uint32_t serial)
+{
+    xdg_wm_base_pong(xdg, serial);
+}
+
+static const struct xdg_wm_base_listener shell_listener_xdg = {
+    handle_ping_xdg_wm_base
+};
+
+
+static void
 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
                       const char *interface, uint32_t version)
 {
@@ -339,6 +351,9 @@
         Wayland_add_display(d, id);
     } else if (strcmp(interface, "wl_seat") == 0) {
         Wayland_display_add_input(d, id);
+    } else if (strcmp(interface, "xdg_wm_base") == 0) {
+        d->shell.xdg = wl_registry_bind(d->registry, id, &xdg_wm_base_interface, 1);
+        xdg_wm_base_add_listener(d->shell.xdg, &shell_listener_xdg, NULL);
     } else if (strcmp(interface, "zxdg_shell_v6") == 0) {
         d->shell.zxdg = wl_registry_bind(d->registry, id, &zxdg_shell_v6_interface, 1);
         zxdg_shell_v6_add_listener(d->shell.zxdg, &shell_listener_zxdg, NULL);
@@ -475,6 +490,9 @@
     if (data->shell.wl)
         wl_shell_destroy(data->shell.wl);
 
+    if (data->shell.xdg)
+        xdg_wm_base_destroy(data->shell.xdg);
+
     if (data->shell.zxdg)
         zxdg_shell_v6_destroy(data->shell.zxdg);
 
diff --git a/source/src/video/wayland/SDL_waylandvideo.h b/source/src/video/wayland/SDL_waylandvideo.h
index 6ad68de..c16c0bd 100644
--- a/source/src/video/wayland/SDL_waylandvideo.h
+++ b/source/src/video/wayland/SDL_waylandvideo.h
@@ -24,6 +24,16 @@
 #ifndef SDL_waylandvideo_h_
 #define SDL_waylandvideo_h_
 
+
+/*
+!!! FIXME: xdg_wm_base is the stable replacement for zxdg_shell_v6. While it's
+!!! FIXME:  harmless to leave it here, consider deleting the obsolete codepath
+!!! FIXME:  soon, since Wayland (with xdg_wm_base) will probably be mainline
+!!! FIXME:  by the time people are relying on this SDL target. It's available
+!!! FIXME:  in Ubuntu 18.04 (and other distros).
+*/
+
+
 #include <EGL/egl.h>
 #include "wayland-util.h"
 
@@ -44,7 +54,7 @@
     struct wl_cursor_theme *cursor_theme;
     struct wl_pointer *pointer;
     struct {
-        /* !!! FIXME: add stable xdg_shell from 1.12 */
+        struct xdg_wm_base *xdg;
         struct zxdg_shell_v6 *zxdg;
         struct wl_shell *wl;
     } shell;
diff --git a/source/src/video/wayland/SDL_waylandwindow.c b/source/src/video/wayland/SDL_waylandwindow.c
index 684022a..aa72991 100644
--- a/source/src/video/wayland/SDL_waylandwindow.c
+++ b/source/src/video/wayland/SDL_waylandwindow.c
@@ -33,6 +33,7 @@
 #include "SDL_waylanddyn.h"
 #include "SDL_hints.h"
 
+#include "xdg-shell-client-protocol.h"
 #include "xdg-shell-unstable-v6-client-protocol.h"
 
 /* On modern desktops, we probably will use the xdg-shell protocol instead
@@ -78,19 +79,15 @@
         }
     }
 
-    if (width == window->w && height == window->h) {
-        return;
-    }
-
-    window->w = width;
-    window->h = height;
-    WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
-
+    WAYLAND_wl_egl_window_resize(wind->egl_window, width, height, 0, 0);
     region = wl_compositor_create_region(wind->waylandData->compositor);
-    wl_region_add(region, 0, 0, window->w, window->h);
+    wl_region_add(region, 0, 0, width, height);
     wl_surface_set_opaque_region(wind->surface, region);
     wl_region_destroy(region);
-    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, window->w, window->h);
+
+    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, width, height);
+    window->w = width;
+    window->h = height;
 }
 
 static void
@@ -113,13 +110,15 @@
     SDL_WindowData *wind = (SDL_WindowData *)data;
     SDL_Window *window = wind->sdlwindow;
     struct wl_region *region;
+
+    wind->shell_surface.zxdg.initial_configure_seen = SDL_TRUE;
+
     WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
 
     region = wl_compositor_create_region(wind->waylandData->compositor);
     wl_region_add(region, 0, 0, window->w, window->h);
     wl_surface_set_opaque_region(wind->surface, region);
     wl_region_destroy(region);
-    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, window->w, window->h);
     zxdg_surface_v6_ack_configure(zxdg, serial);
 }
 
@@ -130,10 +129,85 @@
 
 static void
 handle_configure_zxdg_toplevel(void *data,
-			  struct zxdg_toplevel_v6 *zxdg_toplevel_v6,
-			  int32_t width,
-			  int32_t height,
-			  struct wl_array *states)
+              struct zxdg_toplevel_v6 *zxdg_toplevel_v6,
+              int32_t width,
+              int32_t height,
+              struct wl_array *states)
+{
+    SDL_WindowData *wind = (SDL_WindowData *)data;
+    SDL_Window *window = wind->sdlwindow;
+
+    /* wl_shell_surface spec states that this is a suggestion.
+       Ignore if less than or greater than max/min size. */
+
+    if (width == 0 || height == 0) {
+        return;
+    }
+
+    if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
+        if ((window->flags & SDL_WINDOW_RESIZABLE)) {
+            if (window->max_w > 0) {
+                width = SDL_min(width, window->max_w);
+            } 
+            width = SDL_max(width, window->min_w);
+
+            if (window->max_h > 0) {
+                height = SDL_min(height, window->max_h);
+            }
+            height = SDL_max(height, window->min_h);
+        } else {
+            return;
+        }
+    }
+
+    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, width, height);
+    window->w = width;
+    window->h = height;
+}
+
+static void
+handle_close_zxdg_toplevel(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel_v6)
+{
+    SDL_WindowData *window = (SDL_WindowData *)data;
+    SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
+}
+
+static const struct zxdg_toplevel_v6_listener toplevel_listener_zxdg = {
+    handle_configure_zxdg_toplevel,
+    handle_close_zxdg_toplevel
+};
+
+
+
+static void
+handle_configure_xdg_shell_surface(void *data, struct xdg_surface *xdg, uint32_t serial)
+{
+    SDL_WindowData *wind = (SDL_WindowData *)data;
+    SDL_Window *window = wind->sdlwindow;
+    struct wl_region *region;
+
+    wind->shell_surface.xdg.initial_configure_seen = SDL_TRUE;
+
+    WAYLAND_wl_egl_window_resize(wind->egl_window, window->w, window->h, 0, 0);
+
+    region = wl_compositor_create_region(wind->waylandData->compositor);
+    wl_region_add(region, 0, 0, window->w, window->h);
+    wl_surface_set_opaque_region(wind->surface, region);
+    wl_region_destroy(region);
+    xdg_surface_ack_configure(xdg, serial);
+}
+
+static const struct xdg_surface_listener shell_surface_listener_xdg = {
+    handle_configure_xdg_shell_surface
+};
+
+
+static void
+handle_configure_xdg_toplevel(void *data,
+              struct xdg_toplevel *xdg_toplevel,
+              int32_t width,
+              int32_t height,
+              struct wl_array *states)
 {
     SDL_WindowData *wind = (SDL_WindowData *)data;
     SDL_Window *window = wind->sdlwindow;
@@ -165,21 +239,24 @@
         return;
     }
 
+    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, width, height);
     window->w = width;
     window->h = height;
 }
 
 static void
-handle_close_zxdg_toplevel(void *data, struct zxdg_toplevel_v6 *zxdg_toplevel_v6)
+handle_close_xdg_toplevel(void *data, struct xdg_toplevel *xdg_toplevel)
 {
     SDL_WindowData *window = (SDL_WindowData *)data;
     SDL_SendWindowEvent(window->sdlwindow, SDL_WINDOWEVENT_CLOSE, 0, 0);
 }
 
-static const struct zxdg_toplevel_v6_listener toplevel_listener_zxdg = {
-    handle_configure_zxdg_toplevel,
-    handle_close_zxdg_toplevel
+static const struct xdg_toplevel_listener toplevel_listener_xdg = {
+    handle_configure_xdg_toplevel,
+    handle_close_xdg_toplevel
 };
+
+
 
 
 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
@@ -254,7 +331,13 @@
     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
     SDL_WindowData *wind = window->driverdata;
 
-    if (viddata->shell.zxdg) {
+    if (viddata->shell.xdg) {
+        if (output) {
+            xdg_toplevel_set_fullscreen(wind->shell_surface.xdg.roleobj.toplevel, output);
+        } else {
+            xdg_toplevel_unset_fullscreen(wind->shell_surface.xdg.roleobj.toplevel);
+        }
+    } else if (viddata->shell.zxdg) {
         if (output) {
             zxdg_toplevel_v6_set_fullscreen(wind->shell_surface.zxdg.roleobj.toplevel, output);
         } else {
@@ -359,7 +442,8 @@
     SDL_WindowData *wind = window->driverdata;
     const SDL_VideoData *viddata = (const SDL_VideoData *) _this->driverdata;
 
-    if (viddata->shell.zxdg) {
+    if (viddata->shell.xdg) {
+    } else if (viddata->shell.zxdg) {
     } else {
         wl_shell_surface_set_toplevel(wind->shell_surface.wl);
     }
@@ -373,7 +457,9 @@
     SDL_WindowData *wind = window->driverdata;
     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
 
-    if (viddata->shell.zxdg) {
+    if (viddata->shell.xdg) {
+        xdg_toplevel_set_maximized(wind->shell_surface.xdg.roleobj.toplevel);
+    } else if (viddata->shell.zxdg) {
         zxdg_toplevel_v6_set_maximized(wind->shell_surface.zxdg.roleobj.toplevel);
     } else {
         wl_shell_surface_set_maximized(wind->shell_surface.wl, NULL);
@@ -414,7 +500,13 @@
         wl_compositor_create_surface(c->compositor);
     wl_surface_set_user_data(data->surface, data);
 
-    if (c->shell.zxdg) {
+    if (c->shell.xdg) {
+        data->shell_surface.xdg.surface = xdg_wm_base_get_xdg_surface(c->shell.xdg, data->surface);
+        /* !!! FIXME: add popup role */
+        data->shell_surface.xdg.roleobj.toplevel = xdg_surface_get_toplevel(data->shell_surface.xdg.surface);
+        xdg_toplevel_add_listener(data->shell_surface.xdg.roleobj.toplevel, &toplevel_listener_xdg, data);
+        xdg_toplevel_set_app_id(data->shell_surface.xdg.roleobj.toplevel, c->classname);
+    } else if (c->shell.zxdg) {
         data->shell_surface.zxdg.surface = zxdg_shell_v6_get_xdg_surface(c->shell.zxdg, data->surface);
         /* !!! FIXME: add popup role */
         data->shell_surface.zxdg.roleobj.toplevel = zxdg_surface_v6_get_toplevel(data->shell_surface.zxdg.surface);
@@ -445,7 +537,12 @@
         return SDL_SetError("failed to create a window surface");
     }
 
-    if (c->shell.zxdg) {
+    if (c->shell.xdg) {
+        if (data->shell_surface.xdg.surface) {
+            xdg_surface_set_user_data(data->shell_surface.xdg.surface, data);
+            xdg_surface_add_listener(data->shell_surface.xdg.surface, &shell_surface_listener_xdg, data);
+        }
+    } else if (c->shell.zxdg) {
         if (data->shell_surface.zxdg.surface) {
             zxdg_surface_v6_set_user_data(data->shell_surface.zxdg.surface, data);
             zxdg_surface_v6_add_listener(data->shell_surface.zxdg.surface, &shell_surface_listener_zxdg, data);
@@ -477,6 +574,24 @@
     wl_surface_commit(data->surface);
     WAYLAND_wl_display_flush(c->display);
 
+    /* we have to wait until the surface gets a "configure" event, or
+       use of this surface will fail. This is a new rule for xdg_shell. */
+    if (c->shell.xdg) {
+        if (data->shell_surface.xdg.surface) {
+            while (!data->shell_surface.xdg.initial_configure_seen) {
+                WAYLAND_wl_display_flush(c->display);
+                WAYLAND_wl_display_dispatch(c->display);
+            }
+        }
+    } else if (c->shell.zxdg) {
+        if (data->shell_surface.zxdg.surface) {
+            while (!data->shell_surface.zxdg.initial_configure_seen) {
+                WAYLAND_wl_display_flush(c->display);
+                WAYLAND_wl_display_dispatch(c->display);
+            }
+        }
+    }
+
     return 0;
 }
 
@@ -500,7 +615,9 @@
     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
     
     if (window->title != NULL) {
-        if (viddata->shell.zxdg) {
+        if (viddata->shell.xdg) {
+            xdg_toplevel_set_title(wind->shell_surface.xdg.roleobj.toplevel, window->title);
+        } else if (viddata->shell.zxdg) {
             zxdg_toplevel_v6_set_title(wind->shell_surface.zxdg.roleobj.toplevel, window->title);
         } else {
             wl_shell_surface_set_title(wind->shell_surface.wl, window->title);
@@ -519,7 +636,14 @@
         SDL_EGL_DestroySurface(_this, wind->egl_surface);
         WAYLAND_wl_egl_window_destroy(wind->egl_window);
 
-        if (data->shell.zxdg) {
+        if (data->shell.xdg) {
+            if (wind->shell_surface.xdg.roleobj.toplevel) {
+                xdg_toplevel_destroy(wind->shell_surface.xdg.roleobj.toplevel);
+            }
+            if (wind->shell_surface.zxdg.surface) {
+                xdg_surface_destroy(wind->shell_surface.xdg.surface);
+            }
+        } else if (data->shell.zxdg) {
             if (wind->shell_surface.zxdg.roleobj.toplevel) {
                 zxdg_toplevel_v6_destroy(wind->shell_surface.zxdg.roleobj.toplevel);
             }
diff --git a/source/src/video/wayland/SDL_waylandwindow.h b/source/src/video/wayland/SDL_waylandwindow.h
index 80d4f31..69b9889 100644
--- a/source/src/video/wayland/SDL_waylandwindow.h
+++ b/source/src/video/wayland/SDL_waylandwindow.h
@@ -37,14 +37,24 @@
         struct zxdg_toplevel_v6 *toplevel;
         struct zxdg_popup_v6 *popup;
     } roleobj;
+    SDL_bool initial_configure_seen;
 } SDL_zxdg_shell_surface;
+
+typedef struct {
+    struct xdg_surface *surface;
+    union {
+        struct xdg_toplevel *toplevel;
+        struct xdg_popup *popup;
+    } roleobj;
+    SDL_bool initial_configure_seen;
+} SDL_xdg_shell_surface;
 
 typedef struct {
     SDL_Window *sdlwindow;
     SDL_VideoData *waylandData;
     struct wl_surface *surface;
     union {
-        /* !!! FIXME: add stable xdg_shell from 1.12 */
+        SDL_xdg_shell_surface xdg;
         SDL_zxdg_shell_surface zxdg;
         struct wl_shell_surface *wl;
     } shell_surface;
diff --git a/source/src/video/windows/SDL_windowsevents.c b/source/src/video/windows/SDL_windowsevents.c
index a5fd006..e961cf5 100644
--- a/source/src/video/windows/SDL_windowsevents.c
+++ b/source/src/video/windows/SDL_windowsevents.c
@@ -228,9 +228,7 @@
         /* Ignore the button click for activation */
         if (!bwParamMousePressed) {
             data->focus_click_pending &= ~SDL_BUTTON(button);
-            if (!data->focus_click_pending) {
-                WIN_UpdateClipCursor(data->window);
-            }
+            WIN_UpdateClipCursor(data->window);
         }
         if (WIN_ShouldIgnoreFocusClick()) {
             return;
@@ -416,10 +414,22 @@
         }
         break;
 
+    case WM_NCACTIVATE:
+        {
+            /* Don't immediately clip the cursor in case we're clicking minimize/maximize buttons */
+            data->skip_update_clipcursor = SDL_TRUE;
+        }
+        break;
+
     case WM_ACTIVATE:
         {
             POINT cursorPos;
             BOOL minimized;
+
+            /* Don't mark the window as shown if it's activated before being shown */
+            if (!IsWindowVisible(hwnd)) {
+                break;
+            }
 
             minimized = HIWORD(wParam);
             if (!minimized && (LOWORD(wParam) != WA_INACTIVE)) {
@@ -460,6 +470,8 @@
                 SDL_ToggleModState(KMOD_CAPS, (GetKeyState(VK_CAPITAL) & 0x0001) != 0);
                 SDL_ToggleModState(KMOD_NUM, (GetKeyState(VK_NUMLOCK) & 0x0001) != 0);
             } else {
+                RECT rect;
+
                 data->in_window_deactivation = SDL_TRUE;
 
                 if (SDL_GetKeyboardFocus() == data->window) {
@@ -467,7 +479,10 @@
                     WIN_ResetDeadKeys();
                 }
 
-                ClipCursor(NULL);
+                if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect) == 0)) {
+                    ClipCursor(NULL);
+                    SDL_zero(data->cursor_clipped_rect);
+                }
 
                 data->in_window_deactivation = SDL_FALSE;
             }
@@ -648,7 +663,7 @@
         break;
 
     case WM_UNICHAR:
-        if ( wParam == UNICODE_NOCHAR ) {
+        if (wParam == UNICODE_NOCHAR) {
             returnCode = 1;
             break;
         }
@@ -656,8 +671,8 @@
     case WM_CHAR:
         {
             char text[5];
-            if ( WIN_ConvertUTF32toUTF8( (UINT32)wParam, text ) ) {
-                SDL_SendKeyboardText( text );
+            if (WIN_ConvertUTF32toUTF8((UINT32)wParam, text)) {
+                SDL_SendKeyboardText(text);
             }
         }
         returnCode = 0;
@@ -1022,6 +1037,20 @@
     }
 }
 
+static void WIN_UpdateClipCursorForWindows()
+{
+    SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    SDL_Window *window;
+
+    if (_this) {
+        for (window = _this->windows; window; window = window->next) {
+            if (window->driverdata) {
+                WIN_UpdateClipCursor(window);
+            }
+        }
+    }
+}
+
 /* A message hook called before TranslateMessage() */
 static SDL_WindowsMessageHook g_WindowsMessageHook = NULL;
 static void *g_WindowsMessageHookData = NULL;
@@ -1067,6 +1096,9 @@
     if ((keystate[SDL_SCANCODE_RSHIFT] == SDL_PRESSED) && !(GetKeyState(VK_RSHIFT) & 0x8000)) {
         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RSHIFT);
     }
+
+    /* Update the clipping rect in case someone else has stolen it */
+    WIN_UpdateClipCursorForWindows();
 }
 
 /* to work around #3931, a bug introduced in Win10 Fall Creators Update (build nr. 16299)
@@ -1094,9 +1126,9 @@
             SDL_zero(info);
             info.dwOSVersionInfoSize = sizeof(info);
             if (getVersionPtr(&info) == 0) { /* STATUS_SUCCESS == 0 */
-                if (   (info.dwMajorVersion == 10 && info.dwMinorVersion == 0 && info.dwBuildNumber >= 16299)
-                    || (info.dwMajorVersion == 10 && info.dwMinorVersion > 0)
-                    || (info.dwMajorVersion > 10) )
+                if ((info.dwMajorVersion == 10 && info.dwMinorVersion == 0 && info.dwBuildNumber >= 16299) ||
+                    (info.dwMajorVersion == 10 && info.dwMinorVersion > 0) ||
+                    (info.dwMajorVersion > 10))
                 {
                     return SDL_TRUE;
                 }
diff --git a/source/src/video/windows/SDL_windowsmessagebox.c b/source/src/video/windows/SDL_windowsmessagebox.c
index 924b412..9ddb9e2 100644
--- a/source/src/video/windows/SDL_windowsmessagebox.c
+++ b/source/src/video/windows/SDL_windowsmessagebox.c
@@ -22,15 +22,51 @@
 
 #if SDL_VIDEO_DRIVER_WINDOWS
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#else
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+#endif
+
 #include "../../core/windows/SDL_windows.h"
 
 #include "SDL_assert.h"
 #include "SDL_windowsvideo.h"
-
+#include "SDL_windowstaskdialog.h"
 
 #ifndef SS_EDITCONTROL
 #define SS_EDITCONTROL  0x2000
 #endif
+
+#ifndef IDOK
+#define IDOK 1
+#endif
+
+#ifndef IDCANCEL
+#define IDCANCEL 2
+#endif
+
+/* Custom dialog return codes */
+#define IDCLOSED 20
+#define IDINVALPTRINIT 50
+#define IDINVALPTRCOMMAND 51
+#define IDINVALPTRSETFOCUS 52
+#define IDINVALPTRDLGITEM 53
+/* First button ID */
+#define IDBUTTONINDEX0 100
+
+#define DLGITEMTYPEBUTTON 0x0080
+#define DLGITEMTYPESTATIC 0x0082
+
+/* Windows only sends the lower 16 bits of the control ID when a button
+ * gets clicked. There are also some predefined and custom IDs that lower
+ * the available number further. 2^16 - 101 buttons should be enough for
+ * everyone, no need to make the code more complex.
+ */
+#define MAX_BUTTONS (0xffff - 100)
+
 
 /* Display a Windows message box */
 
@@ -70,15 +106,79 @@
     Uint8 *data;
     size_t size;
     size_t used;
+    WORD numbuttons;
 } WIN_DialogData;
 
+static SDL_bool GetButtonIndex(const SDL_MessageBoxData *messageboxdata, Uint32 flags, size_t *i)
+{
+    for (*i = 0; *i < (size_t)messageboxdata->numbuttons; ++*i) {
+        if (messageboxdata->buttons[*i].flags & flags) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
 
 static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
 {
+    const SDL_MessageBoxData *messageboxdata;
+    size_t buttonindex;
+
     switch ( iMessage ) {
+    case WM_INITDIALOG:
+        if (lParam == 0) {
+            EndDialog(hDlg, IDINVALPTRINIT);
+            return TRUE;
+        }
+        messageboxdata = (const SDL_MessageBoxData *)lParam;
+        SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);
+
+        if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
+            /* Focus on the first default return-key button */
+            HWND buttonctl = GetDlgItem(hDlg, (int)(IDBUTTONINDEX0 + buttonindex));
+            if (buttonctl == NULL) {
+                EndDialog(hDlg, IDINVALPTRDLGITEM);
+            }
+            PostMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)buttonctl, TRUE);
+        } else {
+            /* Give the focus to the dialog window instead */
+            SetFocus(hDlg);
+        }
+        return FALSE;
+    case WM_SETFOCUS:
+        messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+        if (messageboxdata == NULL) {
+            EndDialog(hDlg, IDINVALPTRSETFOCUS);
+            return TRUE;
+        }
+
+        /* Let the default button be focused if there is one. Otherwise, prevent any initial focus. */
+        if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
+            return FALSE;
+        }
+        return TRUE;
     case WM_COMMAND:
+        messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+        if (messageboxdata == NULL) {
+            EndDialog(hDlg, IDINVALPTRCOMMAND);
+            return TRUE;
+        }
+
         /* Return the ID of the button that was pushed */
-        EndDialog(hDlg, LOWORD(wParam));
+        if (wParam == IDOK) {
+            if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
+                EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex);
+            }
+        } else if (wParam == IDCANCEL) {
+            if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, &buttonindex)) {
+                EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex);
+            } else {
+                /* Closing of window was requested by user or system. It would be rude not to comply. */
+                EndDialog(hDlg, IDCLOSED);
+            }
+        } else if (wParam >= IDBUTTONINDEX0 && (int)wParam - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
+            EndDialog(hDlg, wParam);
+        }
         return TRUE;
 
     default:
@@ -89,15 +189,30 @@
 
 static SDL_bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space)
 {
+    /* Growing memory in 64 KiB steps. */
+    const size_t sizestep = 0x10000;
     size_t size = dialog->size;
 
     if (size == 0) {
-        size = space;
-    } else {
-        while ((dialog->used + space) > size) {
-            size *= 2;
+        /* Start with 4 KiB or a multiple of 64 KiB to fit the data. */
+        size = 0x1000;
+        if (SIZE_MAX - sizestep < space) {
+            size = space;
+        } else if (space > size) {
+            size = (space + sizestep) & ~(sizestep - 1);
         }
+    } else if (SIZE_MAX - dialog->used < space) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    } else if (SIZE_MAX - (dialog->used + space) < sizestep) {
+        /* Close to the maximum. */
+        size = dialog->used + space;
+    } else if (size < dialog->used + space) {
+        /* Round up to the next 64 KiB block. */
+        size = dialog->used + space;
+        size += sizestep - size % sizestep;
     }
+
     if (size > dialog->size) {
         void *data = SDL_realloc(dialog->data, size);
         if (!data) {
@@ -175,7 +290,7 @@
 }
 
 
-static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption)
+static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption, WORD ordinal)
 {
     DLGITEMTEMPLATEEX item;
     WORD marker = 0xFFFF;
@@ -205,32 +320,54 @@
     if (!AddDialogData(dialog, &type, sizeof(type))) {
         return SDL_FALSE;
     }
-    if (!AddDialogString(dialog, caption)) {
-        return SDL_FALSE;
+    if (type == DLGITEMTYPEBUTTON || (type == DLGITEMTYPESTATIC && caption != NULL)) {
+        if (!AddDialogString(dialog, caption)) {
+            return SDL_FALSE;
+        }
+    } else {
+        if (!AddDialogData(dialog, &marker, sizeof(marker))) {
+            return SDL_FALSE;
+        }
+        if (!AddDialogData(dialog, &ordinal, sizeof(ordinal))) {
+            return SDL_FALSE;
+        }
     }
     if (!AddDialogData(dialog, &extraData, sizeof(extraData))) {
         return SDL_FALSE;
+    }
+    if (type == DLGITEMTYPEBUTTON) {
+        dialog->numbuttons++;
     }
     ++dialog->lpDialog->cDlgItems;
 
     return SDL_TRUE;
 }
 
-static SDL_bool AddDialogStatic(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
+static SDL_bool AddDialogStaticText(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
 {
-    DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL;
-    return AddDialogControl(dialog, 0x0082, style, 0, x, y, w, h, -1, text);
+    DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL | WS_GROUP;
+    return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -1, text, 0);
+}
+
+static SDL_bool AddDialogStaticIcon(WIN_DialogData *dialog, int x, int y, int w, int h, Uint16 ordinal)
+{
+    DWORD style = WS_VISIBLE | WS_CHILD | SS_ICON | WS_GROUP;
+    return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -2, NULL, ordinal);
 }
 
 static SDL_bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, SDL_bool isDefault)
 {
-    DWORD style = WS_VISIBLE | WS_CHILD;
+    DWORD style = WS_VISIBLE | WS_CHILD | WS_TABSTOP;
     if (isDefault) {
         style |= BS_DEFPUSHBUTTON;
     } else {
         style |= BS_PUSHBUTTON;
     }
-    return AddDialogControl(dialog, 0x0080, style, 0, x, y, w, h, id, text);
+    /* The first button marks the start of the group. */
+    if (dialog->numbuttons == 0) {
+        style |= WS_GROUP;
+    }
+    return AddDialogControl(dialog, DLGITEMTYPEBUTTON, style, 0, x, y, w, h, IDBUTTONINDEX0 + dialog->numbuttons, text, 0);
 }
 
 static void FreeDialogData(WIN_DialogData *dialog)
@@ -341,17 +478,87 @@
     return dialog;
 }
 
-int
-WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
+/* Escaping ampersands is necessary to disable mnemonics in dialog controls.
+ * The caller provides a char** for dst and a size_t* for dstlen where the
+ * address of the work buffer and its size will be stored. Their values must be
+ * NULL and 0 on the first call. src is the string to be escaped. On error, the
+ * function returns NULL and, on success, returns a pointer to the escaped
+ * sequence as a read-only string that is valid until the next call or until the
+ * work buffer is freed. Once all strings have been processed, it's the caller's
+ * responsibilty to free the work buffer with SDL_free, even on errors.
+ */
+static const char *EscapeAmpersands(char **dst, size_t *dstlen, const char *src)
+{
+    char *newdst;
+    size_t ampcount = 0;
+    size_t srclen = 0;
+
+    if (src == NULL) {
+        return NULL;
+    }
+
+    while (src[srclen]) {
+        if (src[srclen] == '&') {
+            ampcount++;
+        }
+        srclen++;
+    }
+    srclen++;
+
+    if (ampcount == 0) {
+        /* Nothing to do. */
+        return src;
+    }
+    if (SIZE_MAX - srclen < ampcount) {
+        return NULL;
+    }
+    if (*dst == NULL || *dstlen < srclen + ampcount) {
+        /* Allocating extra space in case the next strings are a bit longer. */
+        size_t extraspace = SIZE_MAX - (srclen + ampcount);
+        if (extraspace > 512) {
+            extraspace = 512;
+        }
+        *dstlen = srclen + ampcount + extraspace;
+        SDL_free(*dst);
+        *dst = NULL;
+        newdst = SDL_malloc(*dstlen);
+        if (newdst == NULL) {
+            return NULL;
+        }
+        *dst = newdst;
+    } else {
+        newdst = *dst;
+    }
+
+    /* The escape character is the ampersand itself. */
+    while (srclen--) {
+        if (*src == '&') {
+            *newdst++ = '&';
+        }
+        *newdst++ = *src++;
+    }
+
+    return *dst;
+}
+
+/* This function is called if a Task Dialog is unsupported. */
+static int
+WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
 {
     WIN_DialogData *dialog;
-    int i, x, y;
+    int i, x, y, retval;
     const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
     HFONT DialogFont;
     SIZE Size;
     RECT TextSize;
     wchar_t* wmessage;
     TEXTMETRIC TM;
+    HDC FontDC;
+    INT_PTR result;
+    char *ampescape = NULL;
+    size_t ampescapesize = 0;
+    Uint16 defbuttoncount = 0;
+    Uint16 icon = 0;
 
     HWND ParentWindow = NULL;
 
@@ -359,7 +566,25 @@
     const int ButtonHeight = 26;
     const int TextMargin = 16;
     const int ButtonMargin = 12;
+    const int IconWidth = GetSystemMetrics(SM_CXICON);
+    const int IconHeight = GetSystemMetrics(SM_CYICON);
+    const int IconMargin = 20;
 
+    if (messageboxdata->numbuttons > MAX_BUTTONS) {
+        return SDL_SetError("Number of butons exceeds limit of %d", MAX_BUTTONS);
+    }
+
+    switch (messageboxdata->flags) {
+    case SDL_MESSAGEBOX_ERROR:
+        icon = (Uint16)(size_t)IDI_ERROR;
+        break;
+    case SDL_MESSAGEBOX_WARNING:
+        icon = (Uint16)(size_t)IDI_WARNING;
+        break;
+    case SDL_MESSAGEBOX_INFORMATION:
+        icon = (Uint16)(size_t)IDI_INFORMATION;
+        break;
+    }
 
     /* Jan 25th, 2013 - dant@fleetsa.com
      *
@@ -393,7 +618,7 @@
      * In order to get text dimensions we need to have a DC with the desired font.
      * I'm assuming a dialog box in SDL is rare enough we can to the create.
      */
-    HDC FontDC = CreateCompatibleDC(0);
+    FontDC = CreateCompatibleDC(0);
 
     {
         /* Create a duplicate of the font used in system message boxes. */
@@ -428,11 +653,13 @@
     /* Measure the *pixel* size of the string. */
     wmessage = WIN_UTF8ToString(messageboxdata->message);
     SDL_zero(TextSize);
-    DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT);
+    DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_EDITCONTROL);
 
-    /* Add some padding for hangs, etc. */
-    TextSize.right += 2;
-    TextSize.bottom += 2;
+    /* Add margins and some padding for hangs, etc. */
+    TextSize.left += TextMargin;
+    TextSize.right += TextMargin + 2;
+    TextSize.top += TextMargin;
+    TextSize.bottom += TextMargin + 2;
 
     /* Done with the DC, and the string */
     DeleteDC(FontDC);
@@ -444,9 +671,21 @@
     Size.cx += TextMargin * 2;
     Size.cy += TextMargin * 2;
 
+    /* Make dialog wider and shift text over for the icon. */
+    if (icon) {
+        Size.cx += IconMargin + IconWidth;
+        TextSize.left += IconMargin + IconWidth;
+        TextSize.right += IconMargin + IconWidth;
+    }
+
     /* Ensure the size is wide enough for all of the buttons. */
     if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin)
         Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
+
+    /* Reset the height to the icon size if it is actually bigger than the text. */
+    if (icon && Size.cy < IconMargin * 2 + IconHeight) {
+        Size.cy = IconMargin * 2 + IconHeight;
+    }
 
     /* Add vertical space for the buttons and border. */
     Size.cy += ButtonHeight + TextMargin;
@@ -456,7 +695,12 @@
         return -1;
     }
 
-    if (!AddDialogStatic(dialog, TextMargin, TextMargin, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
+    if (icon && ! AddDialogStaticIcon(dialog, IconMargin, IconMargin, IconWidth, IconHeight, icon)) {
+        FreeDialogData(dialog);
+        return -1;
+    }
+
+    if (!AddDialogStaticText(dialog, TextSize.left, TextSize.top, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
         FreeDialogData(dialog);
         return -1;
     }
@@ -465,19 +709,25 @@
     x = Size.cx - (ButtonWidth + ButtonMargin) * messageboxdata->numbuttons;
     y = Size.cy - ButtonHeight - ButtonMargin;
     for (i = messageboxdata->numbuttons - 1; i >= 0; --i) {
-        SDL_bool isDefault;
+        SDL_bool isdefault = SDL_FALSE;
+        const char *buttontext;
 
         if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
-            isDefault = SDL_TRUE;
-        } else {
-            isDefault = SDL_FALSE;
+            defbuttoncount++;
+            if (defbuttoncount == 1) {
+                isdefault = SDL_TRUE;
+            }
         }
-        if (!AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttons[i].text, buttons[i].buttonid, isDefault)) {
+
+        buttontext = EscapeAmpersands(&ampescape, &ampescapesize, buttons[i].text);
+        if (buttontext == NULL || !AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttontext, buttons[i].buttonid, isdefault)) {
             FreeDialogData(dialog);
+            SDL_free(ampescape);
             return -1;
         }
         x += ButtonWidth + ButtonMargin;
     }
+    SDL_free(ampescape);
 
     /* If we have a parent window, get the Instance and HWND for them
      * so that our little dialog gets exclusive focus at all times. */
@@ -485,10 +735,169 @@
         ParentWindow = ((SDL_WindowData*)messageboxdata->window->driverdata)->hwnd;
     }
 
-    *buttonid = (int)DialogBoxIndirect(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc);
+    result = DialogBoxIndirectParam(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc, (LPARAM)messageboxdata);
+    if (result >= IDBUTTONINDEX0 && result - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
+        *buttonid = messageboxdata->buttons[(messageboxdata->numbuttons - 1) - (result - IDBUTTONINDEX0)].buttonid;
+        retval = 0;
+    } else if (result == IDCLOSED) {
+        /* Dialog window closed by user or system. */
+        /* This could use a special return code. */
+        retval = 0;
+        *buttonid = -1;
+    } else {
+        if (result == 0) {
+            SDL_SetError("Invalid parent window handle");
+        } else if (result == -1) {
+            SDL_SetError("The message box encountered an error.");
+        } else if (result == IDINVALPTRINIT || result == IDINVALPTRSETFOCUS || result == IDINVALPTRCOMMAND) {
+            SDL_SetError("Invalid message box pointer in dialog procedure");
+        } else if (result == IDINVALPTRDLGITEM) {
+            SDL_SetError("Couldn't find dialog control of the default enter-key button");
+        } else {
+            SDL_SetError("An unknown error occured");
+        }
+        retval = -1;
+    }
 
     FreeDialogData(dialog);
-    return 0;
+    return retval;
+}
+
+/* TaskDialogIndirect procedure
+ * This is because SDL targets Windows XP (0x501), so this is not defined in the platform SDK.
+ */
+typedef HRESULT(FAR WINAPI *TASKDIALOGINDIRECTPROC)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
+
+int
+WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
+{
+    HWND ParentWindow = NULL;
+    wchar_t *wmessage;
+    wchar_t *wtitle;
+    TASKDIALOGCONFIG TaskConfig;
+    TASKDIALOG_BUTTON *pButtons;
+    TASKDIALOG_BUTTON *pButton;
+    HMODULE hComctl32;
+    TASKDIALOGINDIRECTPROC pfnTaskDialogIndirect;
+    HRESULT hr;
+    char *ampescape = NULL;
+    size_t ampescapesize = 0;
+    int nButton;
+    int nCancelButton;
+    int i;
+
+    if (SIZE_MAX / sizeof(TASKDIALOG_BUTTON) < messageboxdata->numbuttons) {
+        return SDL_OutOfMemory();
+    }
+
+    /* If we cannot load comctl32.dll use the old messagebox! */
+    hComctl32 = LoadLibrary(TEXT("Comctl32.dll"));
+    if (hComctl32 == NULL) {
+        return WIN_ShowOldMessageBox(messageboxdata, buttonid);
+    }
+
+    /* If TaskDialogIndirect doesn't exist use the old messagebox!
+       This will fail prior to Windows Vista.
+       The manifest file in the application may require targeting version 6 of comctl32.dll, even
+       when we use LoadLibrary here!
+       If you don't want to bother with manifests, put this #pragma in your app's source code somewhere:
+       pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0'  processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+     */
+    pfnTaskDialogIndirect = (TASKDIALOGINDIRECTPROC) GetProcAddress(hComctl32, "TaskDialogIndirect");
+    if (pfnTaskDialogIndirect == NULL) {
+        FreeLibrary(hComctl32);
+        return WIN_ShowOldMessageBox(messageboxdata, buttonid);
+    }
+
+    /* If we have a parent window, get the Instance and HWND for them
+       so that our little dialog gets exclusive focus at all times. */
+    if (messageboxdata->window) {
+        ParentWindow = ((SDL_WindowData *) messageboxdata->window->driverdata)->hwnd;
+    }
+
+    wmessage = WIN_UTF8ToString(messageboxdata->message);
+    wtitle = WIN_UTF8ToString(messageboxdata->title);
+
+    SDL_zero(TaskConfig);
+    TaskConfig.cbSize = sizeof (TASKDIALOGCONFIG);
+    TaskConfig.hwndParent = ParentWindow;
+    TaskConfig.dwFlags = TDF_SIZE_TO_CONTENT;
+    TaskConfig.pszWindowTitle = wtitle;
+    if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
+        TaskConfig.pszMainIcon = TD_ERROR_ICON;
+    } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
+        TaskConfig.pszMainIcon = TD_WARNING_ICON;
+    } else if (messageboxdata->flags & SDL_MESSAGEBOX_INFORMATION) {
+        TaskConfig.pszMainIcon = TD_INFORMATION_ICON;
+    } else {
+        TaskConfig.pszMainIcon = NULL;
+    }
+
+    TaskConfig.pszContent = wmessage;
+    TaskConfig.cButtons = messageboxdata->numbuttons;
+    pButtons = SDL_malloc(sizeof (TASKDIALOG_BUTTON) * messageboxdata->numbuttons);
+    TaskConfig.nDefaultButton = 0;
+    nCancelButton = 0;
+    for (i = 0; i < messageboxdata->numbuttons; i++)
+    {
+        const char *buttontext;
+        pButton = &pButtons[messageboxdata->numbuttons-1-i];
+        if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
+            nCancelButton = messageboxdata->buttons[i].buttonid;
+            pButton->nButtonID = 2;
+        } else {
+            pButton->nButtonID = messageboxdata->buttons[i].buttonid + 1;
+            if (pButton->nButtonID >= 2) {
+                pButton->nButtonID++;
+            }
+        }
+        buttontext = EscapeAmpersands(&ampescape, &ampescapesize, messageboxdata->buttons[i].text);
+        if (buttontext == NULL) {
+            int j;
+            FreeLibrary(hComctl32);
+            SDL_free(ampescape);
+            SDL_free(wmessage);
+            SDL_free(wtitle);
+            for (j = 0; j < i; j++) {
+                SDL_free((wchar_t *) pButtons[j].pszButtonText);
+            }
+            SDL_free(pButtons);
+            return -1;
+        }
+        pButton->pszButtonText = WIN_UTF8ToString(buttontext);
+        if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
+            TaskConfig.nDefaultButton = pButton->nButtonID;
+        }
+    }
+    TaskConfig.pButtons = pButtons;
+
+    /* Show the Task Dialog */
+    hr = pfnTaskDialogIndirect(&TaskConfig, &nButton, NULL, NULL);
+
+    /* Free everything */
+    FreeLibrary(hComctl32);
+    SDL_free(ampescape);
+    SDL_free(wmessage);
+    SDL_free(wtitle);
+    for (i = 0; i < messageboxdata->numbuttons; i++) {
+        SDL_free((wchar_t *) pButtons[i].pszButtonText);
+    }
+    SDL_free(pButtons);
+
+    /* Check the Task Dialog was successful and give the result */
+    if (SUCCEEDED(hr)) {
+        if (nButton == 2) {
+            *buttonid = nCancelButton;
+        } else if (nButton > 2) {
+            *buttonid = nButton-1-1;
+        } else {
+            *buttonid = nButton-1;
+        }
+        return 0;
+    }
+
+    /* We failed showing the Task Dialog, use the old message box! */
+    return WIN_ShowOldMessageBox(messageboxdata, buttonid);
 }
 
 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
diff --git a/source/src/video/windows/SDL_windowsmouse.c b/source/src/video/windows/SDL_windowsmouse.c
index 1ddeae2..eff3160 100644
--- a/source/src/video/windows/SDL_windowsmouse.c
+++ b/source/src/video/windows/SDL_windowsmouse.c
@@ -304,8 +304,6 @@
     mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
 
     SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
-
-    SDL_SetDoubleClickTime(GetDoubleClickTime());
 }
 
 void
diff --git a/source/src/video/windows/SDL_windowsopengl.h b/source/src/video/windows/SDL_windowsopengl.h
index 75b4898..8704411 100644
--- a/source/src/video/windows/SDL_windowsopengl.h
+++ b/source/src/video/windows/SDL_windowsopengl.h
@@ -33,31 +33,31 @@
     SDL_bool HAS_WGL_ARB_create_context_robustness;
     SDL_bool HAS_WGL_ARB_create_context_no_error;
 
-	/* Max version of OpenGL ES context that can be created if the
-	   implementation supports WGL_EXT_create_context_es2_profile.
-	   major = minor = 0 when unsupported.
-	 */
-	struct {
-		int major;
-		int minor;
-	} es_profile_max_supported_version;
+    /* Max version of OpenGL ES context that can be created if the
+       implementation supports WGL_EXT_create_context_es2_profile.
+       major = minor = 0 when unsupported.
+     */
+    struct {
+        int major;
+        int minor;
+    } es_profile_max_supported_version;
 
-	void *(WINAPI * wglGetProcAddress) (const char *proc);
-      HGLRC(WINAPI * wglCreateContext) (HDC hdc);
-      BOOL(WINAPI * wglDeleteContext) (HGLRC hglrc);
-      BOOL(WINAPI * wglMakeCurrent) (HDC hdc, HGLRC hglrc);
-      BOOL(WINAPI * wglShareLists) (HGLRC hglrc1, HGLRC hglrc2);
-      BOOL(WINAPI * wglChoosePixelFormatARB) (HDC hdc,
-                                              const int *piAttribIList,
-                                              const FLOAT * pfAttribFList,
-                                              UINT nMaxFormats,
-                                              int *piFormats,
-                                              UINT * nNumFormats);
-      BOOL(WINAPI * wglGetPixelFormatAttribivARB) (HDC hdc, int iPixelFormat,
-                                                   int iLayerPlane,
-                                                   UINT nAttributes,
-                                                   const int *piAttributes,
-                                                   int *piValues);
+    void *(WINAPI * wglGetProcAddress) (const char *proc);
+    HGLRC(WINAPI * wglCreateContext) (HDC hdc);
+    BOOL(WINAPI * wglDeleteContext) (HGLRC hglrc);
+    BOOL(WINAPI * wglMakeCurrent) (HDC hdc, HGLRC hglrc);
+    BOOL(WINAPI * wglShareLists) (HGLRC hglrc1, HGLRC hglrc2);
+    BOOL(WINAPI * wglChoosePixelFormatARB) (HDC hdc,
+                                            const int *piAttribIList,
+                                            const FLOAT * pfAttribFList,
+                                            UINT nMaxFormats,
+                                            int *piFormats,
+                                            UINT * nNumFormats);
+    BOOL(WINAPI * wglGetPixelFormatAttribivARB) (HDC hdc, int iPixelFormat,
+                                                 int iLayerPlane,
+                                                 UINT nAttributes,
+                                                 const int *piAttributes,
+                                                 int *piValues);
     BOOL (WINAPI * wglSwapIntervalEXT) (int interval);
     int (WINAPI * wglGetSwapIntervalEXT) (void);
 };
diff --git a/source/src/video/windows/SDL_windowstaskdialog.h b/source/src/video/windows/SDL_windowstaskdialog.h
new file mode 100644
index 0000000..a2a9e8a
--- /dev/null
+++ b/source/src/video/windows/SDL_windowstaskdialog.h
@@ -0,0 +1,156 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include <pshpack1.h>
+
+typedef HRESULT(CALLBACK *PFTASKDIALOGCALLBACK)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData);
+
+enum _TASKDIALOG_FLAGS
+{
+    TDF_ENABLE_HYPERLINKS = 0x0001,
+    TDF_USE_HICON_MAIN = 0x0002,
+    TDF_USE_HICON_FOOTER = 0x0004,
+    TDF_ALLOW_DIALOG_CANCELLATION = 0x0008,
+    TDF_USE_COMMAND_LINKS = 0x0010,
+    TDF_USE_COMMAND_LINKS_NO_ICON = 0x0020,
+    TDF_EXPAND_FOOTER_AREA = 0x0040,
+    TDF_EXPANDED_BY_DEFAULT = 0x0080,
+    TDF_VERIFICATION_FLAG_CHECKED = 0x0100,
+    TDF_SHOW_PROGRESS_BAR = 0x0200,
+    TDF_SHOW_MARQUEE_PROGRESS_BAR = 0x0400,
+    TDF_CALLBACK_TIMER = 0x0800,
+    TDF_POSITION_RELATIVE_TO_WINDOW = 0x1000,
+    TDF_RTL_LAYOUT = 0x2000,
+    TDF_NO_DEFAULT_RADIO_BUTTON = 0x4000,
+    TDF_CAN_BE_MINIMIZED = 0x8000,
+    //#if (NTDDI_VERSION >= NTDDI_WIN8)
+    TDF_NO_SET_FOREGROUND = 0x00010000, // Don't call SetForegroundWindow() when activating the dialog
+                                        //#endif // (NTDDI_VERSION >= NTDDI_WIN8)
+                                        TDF_SIZE_TO_CONTENT = 0x01000000  // used by ShellMessageBox to emulate MessageBox sizing behavior
+};
+typedef int TASKDIALOG_FLAGS;                         // Note: _TASKDIALOG_FLAGS is an int
+
+typedef enum _TASKDIALOG_MESSAGES
+{
+    TDM_NAVIGATE_PAGE = WM_USER + 101,
+    TDM_CLICK_BUTTON = WM_USER + 102, // wParam = Button ID
+    TDM_SET_MARQUEE_PROGRESS_BAR = WM_USER + 103, // wParam = 0 (nonMarque) wParam != 0 (Marquee)
+    TDM_SET_PROGRESS_BAR_STATE = WM_USER + 104, // wParam = new progress state
+    TDM_SET_PROGRESS_BAR_RANGE = WM_USER + 105, // lParam = MAKELPARAM(nMinRange, nMaxRange)
+    TDM_SET_PROGRESS_BAR_POS = WM_USER + 106, // wParam = new position
+    TDM_SET_PROGRESS_BAR_MARQUEE = WM_USER + 107, // wParam = 0 (stop marquee), wParam != 0 (start marquee), lparam = speed (milliseconds between repaints)
+    TDM_SET_ELEMENT_TEXT = WM_USER + 108, // wParam = element (TASKDIALOG_ELEMENTS), lParam = new element text (LPCWSTR)
+    TDM_CLICK_RADIO_BUTTON = WM_USER + 110, // wParam = Radio Button ID
+    TDM_ENABLE_BUTTON = WM_USER + 111, // lParam = 0 (disable), lParam != 0 (enable), wParam = Button ID
+    TDM_ENABLE_RADIO_BUTTON = WM_USER + 112, // lParam = 0 (disable), lParam != 0 (enable), wParam = Radio Button ID
+    TDM_CLICK_VERIFICATION = WM_USER + 113, // wParam = 0 (unchecked), 1 (checked), lParam = 1 (set key focus)
+    TDM_UPDATE_ELEMENT_TEXT = WM_USER + 114, // wParam = element (TASKDIALOG_ELEMENTS), lParam = new element text (LPCWSTR)
+    TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE = WM_USER + 115, // wParam = Button ID, lParam = 0 (elevation not required), lParam != 0 (elevation required)
+    TDM_UPDATE_ICON = WM_USER + 116  // wParam = icon element (TASKDIALOG_ICON_ELEMENTS), lParam = new icon (hIcon if TDF_USE_HICON_* was set, PCWSTR otherwise)
+} TASKDIALOG_MESSAGES;
+
+typedef enum _TASKDIALOG_NOTIFICATIONS
+{
+    TDN_CREATED = 0,
+    TDN_NAVIGATED = 1,
+    TDN_BUTTON_CLICKED = 2,            // wParam = Button ID
+    TDN_HYPERLINK_CLICKED = 3,            // lParam = (LPCWSTR)pszHREF
+    TDN_TIMER = 4,            // wParam = Milliseconds since dialog created or timer reset
+    TDN_DESTROYED = 5,
+    TDN_RADIO_BUTTON_CLICKED = 6,            // wParam = Radio Button ID
+    TDN_DIALOG_CONSTRUCTED = 7,
+    TDN_VERIFICATION_CLICKED = 8,             // wParam = 1 if checkbox checked, 0 if not, lParam is unused and always 0
+    TDN_HELP = 9,
+    TDN_EXPANDO_BUTTON_CLICKED = 10            // wParam = 0 (dialog is now collapsed), wParam != 0 (dialog is now expanded)
+} TASKDIALOG_NOTIFICATIONS;
+
+typedef struct _TASKDIALOG_BUTTON
+{
+    int     nButtonID;
+    PCWSTR  pszButtonText;
+} TASKDIALOG_BUTTON;
+
+typedef enum _TASKDIALOG_ELEMENTS
+{
+    TDE_CONTENT,
+    TDE_EXPANDED_INFORMATION,
+    TDE_FOOTER,
+    TDE_MAIN_INSTRUCTION
+} TASKDIALOG_ELEMENTS;
+
+typedef enum _TASKDIALOG_ICON_ELEMENTS
+{
+    TDIE_ICON_MAIN,
+    TDIE_ICON_FOOTER
+} TASKDIALOG_ICON_ELEMENTS;
+
+#define TD_WARNING_ICON         MAKEINTRESOURCEW(-1)
+#define TD_ERROR_ICON           MAKEINTRESOURCEW(-2)
+#define TD_INFORMATION_ICON     MAKEINTRESOURCEW(-3)
+#define TD_SHIELD_ICON          MAKEINTRESOURCEW(-4)
+
+enum _TASKDIALOG_COMMON_BUTTON_FLAGS
+{
+    TDCBF_OK_BUTTON = 0x0001, // selected control return value IDOK
+    TDCBF_YES_BUTTON = 0x0002, // selected control return value IDYES
+    TDCBF_NO_BUTTON = 0x0004, // selected control return value IDNO
+    TDCBF_CANCEL_BUTTON = 0x0008, // selected control return value IDCANCEL
+    TDCBF_RETRY_BUTTON = 0x0010, // selected control return value IDRETRY
+    TDCBF_CLOSE_BUTTON = 0x0020  // selected control return value IDCLOSE
+};
+typedef int TASKDIALOG_COMMON_BUTTON_FLAGS;           // Note: _TASKDIALOG_COMMON_BUTTON_FLAGS is an int
+
+typedef struct _TASKDIALOGCONFIG
+{
+    UINT        cbSize;
+    HWND        hwndParent;                             // incorrectly named, this is the owner window, not a parent.
+    HINSTANCE   hInstance;                              // used for MAKEINTRESOURCE() strings
+    TASKDIALOG_FLAGS                dwFlags;            // TASKDIALOG_FLAGS (TDF_XXX) flags
+    TASKDIALOG_COMMON_BUTTON_FLAGS  dwCommonButtons;    // TASKDIALOG_COMMON_BUTTON (TDCBF_XXX) flags
+    PCWSTR      pszWindowTitle;                         // string or MAKEINTRESOURCE()
+    union
+    {
+        HICON   hMainIcon;
+        PCWSTR  pszMainIcon;
+    } /*DUMMYUNIONNAME*/;
+    PCWSTR      pszMainInstruction;
+    PCWSTR      pszContent;
+    UINT        cButtons;
+    const TASKDIALOG_BUTTON  *pButtons;
+    int         nDefaultButton;
+    UINT        cRadioButtons;
+    const TASKDIALOG_BUTTON  *pRadioButtons;
+    int         nDefaultRadioButton;
+    PCWSTR      pszVerificationText;
+    PCWSTR      pszExpandedInformation;
+    PCWSTR      pszExpandedControlText;
+    PCWSTR      pszCollapsedControlText;
+    union
+    {
+        HICON   hFooterIcon;
+        PCWSTR  pszFooterIcon;
+    } /*DUMMYUNIONNAME2*/;
+    PCWSTR      pszFooter;
+    PFTASKDIALOGCALLBACK pfCallback;
+    LONG_PTR    lpCallbackData;
+    UINT        cxWidth;                                // width of the Task Dialog's client area in DLU's. If 0, Task Dialog will calculate the ideal width.
+} TASKDIALOGCONFIG;
+
+#include <poppack.h>
diff --git a/source/src/video/windows/SDL_windowsvideo.c b/source/src/video/windows/SDL_windowsvideo.c
index 8d45b72..358ab23 100644
--- a/source/src/video/windows/SDL_windowsvideo.c
+++ b/source/src/video/windows/SDL_windowsvideo.c
@@ -63,6 +63,15 @@
     }
 }
 
+static void WIN_SuspendScreenSaver(_THIS)
+{
+    if (_this->suspend_screensaver) {
+        SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
+    } else {
+        SetThreadExecutionState(ES_CONTINUOUS);
+    }
+}
+
 
 /* Windows driver bootstrap functions */
 
@@ -136,6 +145,7 @@
     device->GetDisplayModes = WIN_GetDisplayModes;
     device->SetDisplayMode = WIN_SetDisplayMode;
     device->PumpEvents = WIN_PumpEvents;
+    device->SuspendScreenSaver = WIN_SuspendScreenSaver;
 
     device->CreateSDLWindow = WIN_CreateWindow;
     device->CreateSDLWindowFrom = WIN_CreateWindowFrom;
@@ -164,6 +174,7 @@
     device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
     device->OnWindowEnter = WIN_OnWindowEnter;
     device->SetWindowHitTest = WIN_SetWindowHitTest;
+    device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
 
     device->shape_driver.CreateShaper = Win32_CreateShaper;
     device->shape_driver.SetWindowShape = Win32_SetWindowShape;
diff --git a/source/src/video/windows/SDL_windowsvulkan.c b/source/src/video/windows/SDL_windowsvulkan.c
index c4b34f0..6bb8f2a 100644
--- a/source/src/video/windows/SDL_windowsvulkan.c
+++ b/source/src/video/windows/SDL_windowsvulkan.c
@@ -40,7 +40,7 @@
 {
     VkExtensionProperties *extensions = NULL;
     Uint32 extensionCount = 0;
-	Uint32 i;
+    Uint32 i;
     SDL_bool hasSurfaceExtension = SDL_FALSE;
     SDL_bool hasWin32SurfaceExtension = SDL_FALSE;
     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
diff --git a/source/src/video/windows/SDL_windowswindow.c b/source/src/video/windows/SDL_windowswindow.c
index b082443..45463c4 100644
--- a/source/src/video/windows/SDL_windowswindow.c
+++ b/source/src/video/windows/SDL_windowswindow.c
@@ -97,6 +97,11 @@
         if (window->flags & SDL_WINDOW_RESIZABLE) {
             style |= STYLE_RESIZABLE;
         }
+
+        /* Need to set initialize minimize style, or when we call ShowWindow with WS_MINIMIZE it will activate a random window */
+        if (window->flags & SDL_WINDOW_MINIMIZED) {
+            style |= WS_MINIMIZE;
+        }
     }
     return style;
 }
@@ -215,8 +220,6 @@
             if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) {
                 /* We tried to create a window larger than the desktop and Windows didn't allow it.  Override! */
                 int x, y;
-                int w, h;
-
                 /* Figure out what the window area will be */
                 WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_FALSE);
                 SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, w, h, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE);
@@ -287,9 +290,6 @@
         videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH|TWF_WANTPALM));
     }
 
-    /* Enable dropping files */
-    DragAcceptFiles(hwnd, TRUE);
-
     data->initializing = SDL_FALSE;
 
     /* All done! */
@@ -334,6 +334,10 @@
 
     /* Inform Windows of the frame change so we can respond to WM_NCCALCSIZE */
     SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+
+    if (window->flags & SDL_WINDOW_MINIMIZED) {
+        ShowWindow(hwnd, SW_SHOWMINNOACTIVE);
+    }
 
     if (!(window->flags & SDL_WINDOW_OPENGL)) {
         return 0;
@@ -407,13 +411,11 @@
             SDL_sscanf(hint, "%p", (void**)&otherWindow);
 
             /* Do some error checking on the pointer */
-            if (otherWindow != NULL && otherWindow->magic == &_this->window_magic)
-            {
+            if (otherWindow != NULL && otherWindow->magic == &_this->window_magic) {
                 /* If the otherWindow has SDL_WINDOW_OPENGL set, set it for the new window as well */
-                if (otherWindow->flags & SDL_WINDOW_OPENGL)
-                {
+                if (otherWindow->flags & SDL_WINDOW_OPENGL) {
                     window->flags |= SDL_WINDOW_OPENGL;
-                    if(!WIN_GL_SetPixelFormatFrom(_this, otherWindow, window)) {
+                    if (!WIN_GL_SetPixelFormatFrom(_this, otherWindow, window)) {
                         return -1;
                     }
                 }
@@ -546,8 +548,17 @@
 void
 WIN_ShowWindow(_THIS, SDL_Window * window)
 {
-    HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
-    ShowWindow(hwnd, SW_SHOW);
+    DWORD style;
+    HWND hwnd;
+    int nCmdShow;
+    
+    hwnd = ((SDL_WindowData *)window->driverdata)->hwnd;
+    nCmdShow = SW_SHOW;
+    style = GetWindowLong(hwnd, GWL_EXSTYLE);
+    if (style & WS_EX_NOACTIVATE) {
+        nCmdShow = SW_SHOWNOACTIVATE;
+    }
+    ShowWindow(hwnd, nCmdShow);
 }
 
 void
@@ -891,8 +902,13 @@
 {
     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
     SDL_Mouse *mouse = SDL_GetMouse();
+    RECT rect;
 
-    if (data->focus_click_pending) {
+    if (data->in_title_click || data->focus_click_pending) {
+        return;
+    }
+    if (data->skip_update_clipcursor) {
+        data->skip_update_clipcursor = SDL_FALSE;
         return;
     }
 
@@ -900,7 +916,6 @@
         (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
         if (mouse->relative_mode && !mouse->relative_mode_warp) {
             LONG cx, cy;
-            RECT rect;
             GetWindowRect(data->hwnd, &rect);
 
             cx = (rect.left + rect.right) / 2;
@@ -912,17 +927,21 @@
             rect.top = cy - 1;
             rect.bottom = cy + 1;
 
-            ClipCursor(&rect);
+            if (ClipCursor(&rect)) {
+                data->cursor_clipped_rect = rect;
+            }
         } else {
-            RECT rect;
             if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) {
                 ClientToScreen(data->hwnd, (LPPOINT) & rect);
                 ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
-                ClipCursor(&rect);
+                if (ClipCursor(&rect)) {
+                    data->cursor_clipped_rect = rect;
+                }
             }
         }
-    } else {
+    } else if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) {
         ClipCursor(NULL);
+        SDL_zero(data->cursor_clipped_rect);
     }
 }
 
@@ -965,6 +984,13 @@
     return 0;
 }
 
+void
+WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
+{
+    const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
+}
+
 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/windows/SDL_windowswindow.h b/source/src/video/windows/SDL_windowswindow.h
index 0325abb..b738c34 100644
--- a/source/src/video/windows/SDL_windowswindow.h
+++ b/source/src/video/windows/SDL_windowswindow.h
@@ -44,8 +44,10 @@
     SDL_bool in_border_change;
     SDL_bool in_title_click;
     Uint8 focus_click_pending;
+    SDL_bool skip_update_clipcursor;
     SDL_bool windowed_mode_was_maximized;
     SDL_bool in_window_deactivation;
+    RECT cursor_clipped_rect;
     struct SDL_VideoData *videodata;
 #if SDL_VIDEO_OPENGL_EGL  
     EGLSurface egl_surface;
@@ -78,6 +80,7 @@
 extern void WIN_OnWindowEnter(_THIS, SDL_Window * window);
 extern void WIN_UpdateClipCursor(SDL_Window *window);
 extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
+extern void WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
 
 #endif /* SDL_windowswindow_h_ */
 
diff --git a/source/src/video/x11/SDL_x11framebuffer.h b/source/src/video/x11/SDL_x11framebuffer.h
index 61bb0c5..6a31788 100644
--- a/source/src/video/x11/SDL_x11framebuffer.h
+++ b/source/src/video/x11/SDL_x11framebuffer.h
@@ -18,6 +18,10 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
+#ifndef SDL_x11framebuffer_h_
+#define SDL_x11framebuffer_h_
+
 #include "../../SDL_internal.h"
 
 
@@ -28,4 +32,6 @@
                                        const SDL_Rect * rects, int numrects);
 extern void X11_DestroyWindowFramebuffer(_THIS, SDL_Window * window);
 
+#endif /* SDL_x11framebuffer_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/x11/SDL_x11keyboard.c b/source/src/video/x11/SDL_x11keyboard.c
index d667c7b..a57adf9 100644
--- a/source/src/video/x11/SDL_x11keyboard.c
+++ b/source/src/video/x11/SDL_x11keyboard.c
@@ -266,7 +266,7 @@
     int best_distance;
     int best_index;
     int distance;
-    BOOL xkb_repeat = 0;
+    Bool xkb_repeat = 0;
     
     X11_XAutoRepeatOn(data->display);
 
@@ -292,9 +292,7 @@
         char *prev_locale = setlocale(LC_ALL, NULL);
         char *prev_xmods  = X11_XSetLocaleModifiers(NULL);
         const char *new_xmods = "";
-#if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H)
         const char *env_xmods = SDL_getenv("XMODIFIERS");
-#endif
         SDL_bool has_dbus_ime_support = SDL_FALSE;
 
         if (prev_locale) {
@@ -309,16 +307,12 @@
            when it is used via XIM which causes issues. Prevent this by forcing
            @im=none if XMODIFIERS contains @im=ibus. IBus can still be used via 
            the DBus implementation, which also has support for pre-editing. */
-#ifdef HAVE_IBUS_IBUS_H
         if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) {
             has_dbus_ime_support = SDL_TRUE;
         }
-#endif
-#ifdef HAVE_FCITX_FRONTEND_H
         if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) {
             has_dbus_ime_support = SDL_TRUE;
         }
-#endif
         if (has_dbus_ime_support || !xkb_repeat) {
             new_xmods = "@im=none";
         }
diff --git a/source/src/video/x11/SDL_x11messagebox.c b/source/src/video/x11/SDL_x11messagebox.c
index fe6ab19..70a472a 100644
--- a/source/src/video/x11/SDL_x11messagebox.c
+++ b/source/src/video/x11/SDL_x11messagebox.c
@@ -44,7 +44,6 @@
 #endif
 
 #define MAX_BUTTONS             8       /* Maximum number of buttons supported */
-#define MAX_TEXT_LINES          32      /* Maximum number of text lines supported */
 #define MIN_BUTTON_WIDTH        64      /* Minimum button width */
 #define MIN_DIALOG_WIDTH        200     /* Minimum dialog width */
 #define MIN_DIALOG_HEIGHT       100     /* Minimum dialog height */
@@ -101,7 +100,7 @@
     int xtext, ytext;                   /* Text position to start drawing at. */
     int numlines;                       /* Count of Text lines. */
     int text_height;                    /* Height for text lines. */
-    TextLineData linedata[ MAX_TEXT_LINES ];
+    TextLineData *linedata;
 
     int *pbuttonid;                     /* Pointer to user return buttonid value. */
 
@@ -223,6 +222,18 @@
     return 0;
 }
 
+static int
+CountLinesOfText(const char *text)
+{
+    int retval = 0;
+    while (text && *text) {
+        const char *lf = SDL_strchr(text, '\n');
+        retval++;  /* even without an endline, this counts as a line. */
+        text = lf ? lf + 1 : NULL;
+    }
+    return retval;
+}
+
 /* Calculate and initialize text and button locations. */
 static int
 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
@@ -237,29 +248,35 @@
     /* Go over text and break linefeeds into separate lines. */
     if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
         const char *text = messageboxdata->message;
-        TextLineData *plinedata = data->linedata;
+        const int linecount = CountLinesOfText(text);
+        TextLineData *plinedata = (TextLineData *) SDL_malloc(sizeof (TextLineData) * linecount);
 
-        for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
+        if (!plinedata) {
+            return SDL_OutOfMemory();
+        }
+
+        data->linedata = plinedata;
+        data->numlines = linecount;
+
+        for ( i = 0; i < linecount; i++, plinedata++ ) {
+            const char *lf = SDL_strchr( text, '\n' );
+            const int length = lf ? ( lf - text ) : SDL_strlen( text );
             int height;
-            char *lf = SDL_strchr( ( char * )text, '\n' );
 
-            data->numlines++;
-
-            /* Only grab length up to lf if it exists and isn't the last line. */
-            plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
             plinedata->text = text;
 
-            GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
+            GetTextWidthHeight( data, text, length, &plinedata->width, &height );
 
             /* Text and widths are the largest we've ever seen. */
             data->text_height = IntMax( data->text_height, height );
             text_width_max = IntMax( text_width_max, plinedata->width );
 
+            plinedata->length = length;
             if (lf && (lf > text) && (lf[-1] == '\r')) {
                 plinedata->length--;
             }
 
-            text += plinedata->length + 1;
+            text += length + 1;
 
             /* Break if there are no more linefeeds. */
             if ( !lf )
@@ -369,6 +386,8 @@
         X11_XCloseDisplay( data->display );
         data->display = NULL;
     }
+
+    SDL_free(data->linedata);
 }
 
 /* Create and set up our X11 dialog box indow. */
diff --git a/source/src/video/x11/SDL_x11messagebox.h b/source/src/video/x11/SDL_x11messagebox.h
index cab407b..6515983 100644
--- a/source/src/video/x11/SDL_x11messagebox.h
+++ b/source/src/video/x11/SDL_x11messagebox.h
@@ -19,10 +19,15 @@
   3. This notice may not be removed or altered from any source distribution.
 */
 
+#ifndef SDL_x11messagebox_h_
+#define SDL_x11messagebox_h_
+
 #if SDL_VIDEO_DRIVER_X11
 
 extern int X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
 
 #endif /* SDL_VIDEO_DRIVER_X11 */
 
+#endif /* SDL_x11messagebox_h_ */
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/x11/SDL_x11opengl.h b/source/src/video/x11/SDL_x11opengl.h
index 1a26ea0..7331b71 100644
--- a/source/src/video/x11/SDL_x11opengl.h
+++ b/source/src/video/x11/SDL_x11opengl.h
@@ -38,14 +38,14 @@
     SDL_bool HAS_GLX_ARB_create_context_robustness;
     SDL_bool HAS_GLX_ARB_create_context_no_error;
 
-	/* Max version of OpenGL ES context that can be created if the
-	   implementation supports GLX_EXT_create_context_es2_profile.
-	   major = minor = 0 when unsupported.
-	 */
-	struct {
-		int major;
-		int minor;
-	} es_profile_max_supported_version;
+    /* Max version of OpenGL ES context that can be created if the
+       implementation supports GLX_EXT_create_context_es2_profile.
+       major = minor = 0 when unsupported.
+     */
+    struct {
+        int major;
+        int minor;
+    } es_profile_max_supported_version;
 
     Bool (*glXQueryExtension) (Display*,int*,int*);
     void *(*glXGetProcAddress) (const GLubyte*);
diff --git a/source/src/video/x11/SDL_x11sym.h b/source/src/video/x11/SDL_x11sym.h
index a07a030..6709992 100644
--- a/source/src/video/x11/SDL_x11sym.h
+++ b/source/src/video/x11/SDL_x11sym.h
@@ -45,7 +45,7 @@
 SDL_X11_SYM(Colormap,XCreateColormap,(Display* a,Window b,Visual* c,int d),(a,b,c,d),return)
 SDL_X11_SYM(Cursor,XCreatePixmapCursor,(Display* a,Pixmap b,Pixmap c,XColor* d,XColor* e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return)
 SDL_X11_SYM(Cursor,XCreateFontCursor,(Display* a,unsigned int b),(a,b),return)
-SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char**	e),(a,b,c,d,e),return)
+SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char** e),(a,b,c,d,e),return)
 SDL_X11_SYM(GC,XCreateGC,(Display* a,Drawable b,unsigned long c,XGCValues* d),(a,b,c,d),return)
 SDL_X11_SYM(XImage*,XCreateImage,(Display* a,Visual* b,unsigned int c,int d,int e,char* f,unsigned int g,unsigned int h,int i,int j),(a,b,c,d,e,f,g,h,i,j),return)
 SDL_X11_SYM(Window,XCreateWindow,(Display* a,Window b,int c,int d,unsigned int e,unsigned int f,unsigned int g,int h,unsigned int i,Visual* j,unsigned long k,XSetWindowAttributes* l),(a,b,c,d,e,f,g,h,i,j,k,l),return)
@@ -180,7 +180,7 @@
 SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c),(a,b,c),return)
 SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
 SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
-SDL_X11_SYM(BOOL,XkbSetDetectableAutoRepeat,(Display* a, BOOL b, BOOL* c),(a,b,c),return)
+SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c),(a,b,c),return)
 #endif
 
 #if NeedWidePrototypes
@@ -201,7 +201,7 @@
 SDL_X11_SYM(XIM,XOpenIM,(Display* a,struct _XrmHashBucketRec* b,char* c,char* d),(a,b,c,d),return)
 SDL_X11_SYM(Status,XCloseIM,(XIM a),(a),return)
 SDL_X11_SYM(void,Xutf8DrawString,(Display *a, Drawable b, XFontSet c, GC d, int e, int f, _Xconst char *g, int h),(a,b,c,d,e,f,g,h),)
-SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char*	b, int c, XRectangle* d, XRectangle* e),(a,b,c,d,e),return)
+SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle* d, XRectangle* e),(a,b,c,d,e),return)
 SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a),(a),return)
 SDL_X11_SYM(char*,Xutf8ResetIC,(XIC a),(a),return)
 #endif
diff --git a/source/src/video/x11/SDL_x11video.c b/source/src/video/x11/SDL_x11video.c
index b8f8edf..b3b1a70 100644
--- a/source/src/video/x11/SDL_x11video.c
+++ b/source/src/video/x11/SDL_x11video.c
@@ -260,6 +260,7 @@
     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
     device->GetWindowWMInfo = X11_GetWindowWMInfo;
     device->SetWindowHitTest = X11_SetWindowHitTest;
+    device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
 
     device->shape_driver.CreateShaper = X11_CreateShaper;
     device->shape_driver.SetWindowShape = X11_SetWindowShape;
diff --git a/source/src/video/x11/SDL_x11window.c b/source/src/video/x11/SDL_x11window.c
index be03aa6..0a254b0 100644
--- a/source/src/video/x11/SDL_x11window.c
+++ b/source/src/video/x11/SDL_x11window.c
@@ -390,7 +390,6 @@
     const char *wintype_name = NULL;
     long compositor = 1;
     Atom _NET_WM_PID;
-    Atom XdndAware, xdnd_version = 5;
     long fevent = 0;
 
 #if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_EGL
@@ -650,11 +649,6 @@
                  PointerMotionMask | KeyPressMask | KeyReleaseMask |
                  PropertyChangeMask | StructureNotifyMask |
                  KeymapStateMask | fevent));
-
-    XdndAware = X11_XInternAtom(display, "XdndAware", False);
-    X11_XChangeProperty(display, w, XdndAware, XA_ATOM, 32,
-                 PropModeReplace,
-                 (unsigned char*)&xdnd_version, 1);
 
     X11_XFlush(display);
 
@@ -1604,6 +1598,22 @@
     return 0;  /* just succeed, the real work is done elsewhere. */
 }
 
+void
+X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    Display *display = data->videodata->display;
+    Atom XdndAware = X11_XInternAtom(display, "XdndAware", False);
+
+    if (accept) {
+        Atom xdnd_version = 5;
+        X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32,
+                     PropModeReplace, (unsigned char*)&xdnd_version, 1);
+    } else {
+        X11_XDeleteProperty(display, data->xwindow, XdndAware);
+    }
+}
+
 #endif /* SDL_VIDEO_DRIVER_X11 */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/src/video/x11/SDL_x11window.h b/source/src/video/x11/SDL_x11window.h
index 7c4c6c5..6ee8016 100644
--- a/source/src/video/x11/SDL_x11window.h
+++ b/source/src/video/x11/SDL_x11window.h
@@ -104,6 +104,7 @@
 extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
                                     struct SDL_SysWMinfo *info);
 extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
+extern void X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
 
 #endif /* SDL_x11window_h_ */
 
diff --git a/source/src/video/yuv2rgb/yuv_rgb_std_func.h b/source/src/video/yuv2rgb/yuv_rgb_std_func.h
index bf4f48e..f0ab5c6 100644
--- a/source/src/video/yuv2rgb/yuv_rgb_std_func.h
+++ b/source/src/video/yuv2rgb/yuv_rgb_std_func.h
@@ -77,20 +77,20 @@
 {
 	const YUV2RGBParam *const param = &(YUV2RGB[yuv_type]);
 #if YUV_FORMAT == YUV_FORMAT_420
-	const int y_pixel_stride = 1;
-	const int uv_pixel_stride = 1;
-	const int uv_x_sample_interval = 2;
-	const int uv_y_sample_interval = 2;
+	#define y_pixel_stride 1
+	#define uv_pixel_stride 1
+	#define uv_x_sample_interval 2
+	#define uv_y_sample_interval 2
 #elif YUV_FORMAT == YUV_FORMAT_422
-	const int y_pixel_stride = 2;
-	const int uv_pixel_stride = 4;
-	const int uv_x_sample_interval = 2;
-	const int uv_y_sample_interval = 1;
+	#define y_pixel_stride 2
+	#define uv_pixel_stride 4
+	#define uv_x_sample_interval 2
+	#define uv_y_sample_interval 1
 #elif YUV_FORMAT == YUV_FORMAT_NV12
-	const int y_pixel_stride = 1;
-	const int uv_pixel_stride = 2;
-	const int uv_x_sample_interval = 2;
-	const int uv_y_sample_interval = 2;
+	#define y_pixel_stride 1
+	#define uv_pixel_stride 2
+	#define uv_x_sample_interval 2
+	#define uv_y_sample_interval 2
 #endif
 
 	uint32_t x, y;
@@ -101,9 +101,12 @@
 			*u_ptr=U+(y/uv_y_sample_interval)*UV_stride,
 			*v_ptr=V+(y/uv_y_sample_interval)*UV_stride;
 		
-		uint8_t *rgb_ptr1=RGB+y*RGB_stride,
-			*rgb_ptr2=RGB+(y+1)*RGB_stride;
-		
+		uint8_t *rgb_ptr1=RGB+y*RGB_stride;
+
+		#if uv_y_sample_interval > 1
+        uint8_t *rgb_ptr2=RGB+(y+1)*RGB_stride;
+		#endif
+
 		for(x=0; x<(width-(uv_x_sample_interval-1)); x+=uv_x_sample_interval)
 		{
 			// Compute U and V contributions, common to the four pixels
@@ -123,13 +126,13 @@
 			y_tmp = ((y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor);
 			PACK_PIXEL(rgb_ptr1);
 			
-			if (uv_y_sample_interval > 1) {
-				y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor);
-				PACK_PIXEL(rgb_ptr2);
+			#if uv_y_sample_interval > 1
+			y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor);
+			PACK_PIXEL(rgb_ptr2);
 				
-				y_tmp = ((y_ptr2[y_pixel_stride]-param->y_shift)*param->y_factor);
-				PACK_PIXEL(rgb_ptr2);
-			}
+			y_tmp = ((y_ptr2[y_pixel_stride]-param->y_shift)*param->y_factor);
+			PACK_PIXEL(rgb_ptr2);
+			#endif
 
 			y_ptr1+=2*y_pixel_stride;
 			y_ptr2+=2*y_pixel_stride;
@@ -154,10 +157,10 @@
 			int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor);
 			PACK_PIXEL(rgb_ptr1);
 			
-			if (uv_y_sample_interval > 1) {
-				y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor);
-				PACK_PIXEL(rgb_ptr2);
-			}
+			#if uv_y_sample_interval > 1
+			y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor);
+			PACK_PIXEL(rgb_ptr2);
+			#endif
 		}
 	}
 
@@ -212,6 +215,11 @@
 			PACK_PIXEL(rgb_ptr1);
 		}
 	}
+
+	#undef y_pixel_stride
+	#undef uv_pixel_stride
+	#undef uv_x_sample_interval
+	#undef uv_y_sample_interval
 }
 
 #undef STD_FUNCTION_NAME
diff --git a/source/test/Makefile.in b/source/test/Makefile.in
index bc9c24a..9ddd6ff 100644
--- a/source/test/Makefile.in
+++ b/source/test/Makefile.in
@@ -55,6 +55,7 @@
 	testrumble$(EXE) \
 	testscale$(EXE) \
 	testsem$(EXE) \
+	testsensor$(EXE) \
 	testshader$(EXE) \
 	testshape$(EXE) \
 	testsprite2$(EXE) \
@@ -240,6 +241,9 @@
 testsem$(EXE): $(srcdir)/testsem.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
+testsensor$(EXE): $(srcdir)/testsensor.c
+	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+
 testshader$(EXE): $(srcdir)/testshader.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS) @GLLIB@ @MATHLIB@
 
@@ -313,7 +317,10 @@
 %.wav: $(srcdir)/%.wav
 	cp $< $@
 
-copydatafiles: copybmpfiles copywavfiles
+%.dat: $(srcdir)/%.dat
+	cp $< $@
+
+copydatafiles: copybmpfiles copywavfiles copydatfiles
 .PHONY : copydatafiles
 
 copybmpfiles: $(foreach bmp,$(wildcard $(srcdir)/*.bmp),$(notdir $(bmp)))
@@ -322,3 +329,6 @@
 copywavfiles: $(foreach wav,$(wildcard $(srcdir)/*.wav),$(notdir $(wav)))
 .PHONY : copywavfiles
 
+copydatfiles: $(foreach dat,$(wildcard $(srcdir)/*.dat),$(notdir $(dat)))
+.PHONY : copydatfiles
+
diff --git a/source/test/testcustomcursor.c b/source/test/testcustomcursor.c
index b99a10b..4694498 100644
--- a/source/test/testcustomcursor.c
+++ b/source/test/testcustomcursor.c
@@ -73,6 +73,24 @@
     SDL_Cursor *cursor = NULL;
     SDL_Surface *surface = SDL_LoadBMP(file);
     if (surface) {
+        if (surface->format->palette) {
+            SDL_SetColorKey(surface, 1, *(Uint8 *) surface->pixels);
+        } else {
+            switch (surface->format->BitsPerPixel) {
+            case 15:
+                SDL_SetColorKey(surface, 1, (*(Uint16 *)surface->pixels) & 0x00007FFF);
+                break;
+            case 16:
+                SDL_SetColorKey(surface, 1, *(Uint16 *)surface->pixels);
+                break;
+            case 24:
+                SDL_SetColorKey(surface, 1, (*(Uint32 *)surface->pixels) & 0x00FFFFFF);
+                break;
+            case 32:
+                SDL_SetColorKey(surface, 1, *(Uint32 *)surface->pixels);
+                break;
+            }
+        }
         cursor = SDL_CreateColorCursor(surface, 0, 0);
         SDL_FreeSurface(surface);
     }
@@ -116,7 +134,9 @@
 
 static SDLTest_CommonState *state;
 int done;
-SDL_Cursor *cursor = NULL;
+static SDL_Cursor *cursors[1+SDL_NUM_SYSTEM_CURSORS];
+static int current_cursor;
+static int show_cursor;
 
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
@@ -134,6 +154,18 @@
     /* Check for events */
     while (SDL_PollEvent(&event)) {
         SDLTest_CommonEvent(state, &event, &done);
+        if (event.type == SDL_MOUSEBUTTONDOWN) {
+            if (event.button.button == SDL_BUTTON_LEFT) {
+                ++current_cursor;
+                if (current_cursor == SDL_arraysize(cursors)) {
+                    current_cursor = 0;
+                }
+                SDL_SetCursor(cursors[current_cursor]);
+            } else {
+                show_cursor = !show_cursor;
+                SDL_ShowCursor(show_cursor);
+            }
+        }
     }
     
     for (i = 0; i < state->num_windows; ++i) {
@@ -188,15 +220,22 @@
     }
 
     if (color_cursor) {
-        cursor = init_color_cursor(color_cursor);
+        cursors[0] = init_color_cursor(color_cursor);
     } else {
-        cursor = init_system_cursor(arrow);
+        cursors[0] = init_system_cursor(arrow);
     }
-    if (!cursor) {
+    if (!cursors[0]) {
         SDL_Log("Error, couldn't create cursor\n");
         quit(2);
     }
-    SDL_SetCursor(cursor);
+    for (i = 0; i < SDL_NUM_SYSTEM_CURSORS; ++i) {
+        cursors[1+i] = SDL_CreateSystemCursor((SDL_SystemCursor)i);
+        if (!cursors[1+i]) {
+            SDL_Log("Error, couldn't create system cursor %d\n", i);
+            quit(2);
+        }
+    }
+    SDL_SetCursor(cursors[0]);
 
     /* Main render loop */
     done = 0;
@@ -208,9 +247,13 @@
     }
 #endif
 
-    SDL_FreeCursor(cursor);
+    for (i = 0; i < SDL_arraysize(cursors); ++i) {
+        SDL_FreeCursor(cursors[i]);
+    }
     quit(0);
 
     /* keep the compiler happy ... */
     return(0);
 }
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/source/test/testgamecontroller.c b/source/test/testgamecontroller.c
index ef3a02b..c8616d7 100644
--- a/source/test/testgamecontroller.c
+++ b/source/test/testgamecontroller.c
@@ -114,6 +114,11 @@
         case SDL_CONTROLLERBUTTONDOWN:
         case SDL_CONTROLLERBUTTONUP:
             SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
+            /* First button triggers a 0.5 second full strength rumble */
+            if (event.type == SDL_CONTROLLERBUTTONDOWN &&
+                event.cbutton.button == SDL_CONTROLLER_BUTTON_A) {
+                SDL_GameControllerRumble(gamecontroller, 0xFFFF, 0xFFFF, 500);
+            }
             break;
         case SDL_KEYDOWN:
             if (event.key.keysym.sym != SDLK_ESCAPE) {
diff --git a/source/test/testjoystick.c b/source/test/testjoystick.c
index d2d1c4e..bca7492 100644
--- a/source/test/testjoystick.c
+++ b/source/test/testjoystick.c
@@ -90,6 +90,10 @@
             case SDL_JOYBUTTONDOWN:
                 SDL_Log("Joystick %d button %d down\n",
                        event.jbutton.which, event.jbutton.button);
+                /* First button triggers a 0.5 second full strength rumble */
+                if (event.jbutton.button == 0) {
+                    SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500);
+                }
                 break;
             case SDL_JOYBUTTONUP:
                 SDL_Log("Joystick %d button %d up\n",
diff --git a/source/test/testplatform.c b/source/test/testplatform.c
index 84efab3..1c1d2dc 100644
--- a/source/test/testplatform.c
+++ b/source/test/testplatform.c
@@ -380,6 +380,7 @@
         SDL_Log("SSE4.2 %s\n", SDL_HasSSE42()? "detected" : "not detected");
         SDL_Log("AVX %s\n", SDL_HasAVX()? "detected" : "not detected");
         SDL_Log("AVX2 %s\n", SDL_HasAVX2()? "detected" : "not detected");
+        SDL_Log("AVX-512F %s\n", SDL_HasAVX512F()? "detected" : "not detected");
         SDL_Log("NEON %s\n", SDL_HasNEON()? "detected" : "not detected");
         SDL_Log("System RAM %d MB\n", SDL_GetSystemRAM());
     }
diff --git a/source/test/testresample.c b/source/test/testresample.c
index bcdaa66..4234d9e 100644
--- a/source/test/testresample.c
+++ b/source/test/testresample.c
@@ -93,7 +93,7 @@
     SDL_WriteLE32(io, 0x45564157);      /* WAVE */
     SDL_WriteLE32(io, 0x20746D66);      /* fmt */
     SDL_WriteLE32(io, 16);      /* chunk size */
-    SDL_WriteLE16(io, 1);       /* uncompressed */
+    SDL_WriteLE16(io, SDL_AUDIO_ISFLOAT(spec.format) ? 3 : 1);       /* uncompressed */
     SDL_WriteLE16(io, cvtchans);   /* channels */
     SDL_WriteLE32(io, cvtfreq); /* sample rate */
     SDL_WriteLE32(io, avgbytes);        /* average bytes per second */
diff --git a/source/test/testsensor.c b/source/test/testsensor.c
new file mode 100644
index 0000000..00bfd13
--- /dev/null
+++ b/source/test/testsensor.c
@@ -0,0 +1,117 @@
+/*
+  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely.
+*/
+
+/* Simple test of the SDL sensor code */
+
+#include "SDL.h"
+
+static const char *GetSensorTypeString(SDL_SensorType type)
+{
+    static char unknown_type[64];
+
+    switch (type)
+    {
+    case SDL_SENSOR_INVALID:
+        return "SDL_SENSOR_INVALID";
+    case SDL_SENSOR_UNKNOWN:
+        return "SDL_SENSOR_UNKNOWN";
+    case SDL_SENSOR_ACCEL:
+        return "SDL_SENSOR_ACCEL";
+    case SDL_SENSOR_GYRO:
+        return "SDL_SENSOR_GYRO";
+    default:
+        SDL_snprintf(unknown_type, sizeof(unknown_type), "UNKNOWN (%d)", type);
+        return unknown_type;
+    }
+}
+
+static void HandleSensorEvent(SDL_SensorEvent *event)
+{
+    SDL_Sensor *sensor = SDL_SensorFromInstanceID(event->which);
+    if (!sensor) {
+        SDL_Log("Couldn't get sensor for sensor event\n");
+        return;
+    }
+
+    switch (SDL_SensorGetType(sensor)) {
+    case SDL_SENSOR_ACCEL:
+        SDL_Log("Accelerometer update: %.2f, %.2f, %.2f\n", event->data[0], event->data[1], event->data[2]);
+        break;
+    case SDL_SENSOR_GYRO:
+        SDL_Log("Gyro update: %.2f, %.2f, %.2f\n", event->data[0], event->data[1], event->data[2]);
+        break;
+    default:
+        SDL_Log("Sensor update for sensor type %s\n", GetSensorTypeString(SDL_SensorGetType(sensor)));
+        break;
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    int i;
+    int num_sensors, num_opened;
+
+    /* Load the SDL library */
+    if (SDL_Init(SDL_INIT_SENSOR) < 0) {
+        SDL_Log("Couldn't initialize SDL: %s\n", SDL_GetError());
+        return (1);
+    }
+
+    num_sensors = SDL_NumSensors();
+    num_opened = 0;
+
+    SDL_Log("There are %d sensors available\n", num_sensors);
+    for (i = 0; i < num_sensors; ++i) {
+        SDL_Log("Sensor %d: %s, type %s, platform type %d\n",
+            SDL_SensorGetDeviceInstanceID(i),
+            SDL_SensorGetDeviceName(i),
+            GetSensorTypeString(SDL_SensorGetDeviceType(i)),
+            SDL_SensorGetDeviceNonPortableType(i));
+
+        if (SDL_SensorGetDeviceType(i) != SDL_SENSOR_UNKNOWN) {
+            SDL_Sensor *sensor = SDL_SensorOpen(i);
+            if (sensor == NULL) {
+                SDL_Log("Couldn't open sensor %d: %s\n", SDL_SensorGetDeviceInstanceID(i), SDL_GetError());
+            } else {
+                ++num_opened;
+            }
+        }
+    }
+    SDL_Log("Opened %d sensors\n", num_opened);
+
+    if (num_opened > 0) {
+        SDL_bool done = SDL_FALSE;
+        SDL_Event event;
+
+        SDL_CreateWindow("Sensor Test", 0, 0, 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
+        while (!done) {
+            while (SDL_PollEvent(&event) > 0) {
+                switch (event.type) {
+                case SDL_SENSORUPDATE:
+                    HandleSensorEvent(&event.sensor);
+                    break;
+                case SDL_MOUSEBUTTONUP:
+                case SDL_KEYUP:
+                case SDL_QUIT:
+                    done = SDL_TRUE;
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+    }
+
+    SDL_Quit();
+    return (0);
+}
diff --git a/source/test/testthread.c b/source/test/testthread.c
index cdccfc4..4555a1e 100644
--- a/source/test/testthread.c
+++ b/source/test/testthread.c
@@ -20,6 +20,7 @@
 
 static SDL_TLSID tls;
 static int alive = 0;
+static int testprio = 0;
 
 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
 static void
@@ -29,14 +30,37 @@
     exit(rc);
 }
 
+static const char *
+getprioritystr(SDL_ThreadPriority priority)
+{
+    switch(priority)
+    {
+    case SDL_THREAD_PRIORITY_LOW: return "SDL_THREAD_PRIORITY_LOW";
+    case SDL_THREAD_PRIORITY_NORMAL: return "SDL_THREAD_PRIORITY_NORMAL";
+    case SDL_THREAD_PRIORITY_HIGH: return "SDL_THREAD_PRIORITY_HIGH";
+    case SDL_THREAD_PRIORITY_TIME_CRITICAL: return "SDL_THREAD_PRIORITY_TIME_CRITICAL";
+    }
+
+    return "???";
+}
+
 int SDLCALL
 ThreadFunc(void *data)
 {
+    SDL_ThreadPriority prio = SDL_THREAD_PRIORITY_NORMAL;
+
     SDL_TLSSet(tls, "baby thread", NULL);
     SDL_Log("Started thread %s: My thread id is %lu, thread data = %s\n",
            (char *) data, SDL_ThreadID(), (const char *)SDL_TLSGet(tls));
     while (alive) {
         SDL_Log("Thread '%s' is alive!\n", (char *) data);
+
+        if (testprio) {
+            SDL_Log("SDL_SetThreadPriority(%s):%d\n", getprioritystr(prio), SDL_SetThreadPriority(prio));
+            if (++prio > SDL_THREAD_PRIORITY_TIME_CRITICAL)
+                prio = SDL_THREAD_PRIORITY_LOW;
+        }
+
         SDL_Delay(1 * 1000);
     }
     SDL_Log("Thread '%s' exiting!\n", (char *) data);
@@ -55,6 +79,7 @@
 int
 main(int argc, char *argv[])
 {
+    int arg = 1;
     SDL_Thread *thread;
 
     /* Enable standard application logging */
@@ -66,6 +91,13 @@
         return (1);
     }
 
+    while (argv[arg] && *argv[arg] == '-') {
+        if (SDL_strcmp(argv[arg], "--prio") == 0) {
+            testprio = 1;
+        }
+        ++arg;
+    }
+
     tls = SDL_TLSCreate();
     SDL_assert(tls);
     SDL_TLSSet(tls, "main thread", NULL);
diff --git a/source/test/testvulkan.c b/source/test/testvulkan.c
index 73a2185..cd682af 100644
--- a/source/test/testvulkan.c
+++ b/source/test/testvulkan.c
@@ -255,7 +255,7 @@
     appInfo.apiVersion = VK_API_VERSION_1_0;
     instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
     instanceCreateInfo.pApplicationInfo = &appInfo;
-    if(!SDL_Vulkan_GetInstanceExtensions(state->windows[0], &extensionCount, NULL))
+    if(!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, NULL))
     {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                      "SDL_Vulkan_GetInstanceExtensions(): %s\n",
@@ -268,7 +268,7 @@
         SDL_OutOfMemory();
         quit(2);
     }
-    if(!SDL_Vulkan_GetInstanceExtensions(state->windows[0], &extensionCount, extensions))
+    if(!SDL_Vulkan_GetInstanceExtensions(NULL, &extensionCount, extensions))
     {
         SDL_free((void*)extensions);
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
diff --git a/source/test/testwm2.c b/source/test/testwm2.c
index 74a0fe0..5da3873 100644
--- a/source/test/testwm2.c
+++ b/source/test/testwm2.c
@@ -146,6 +146,9 @@
         quit(2);
     }
 
+    SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
+    SDL_EventState(SDL_DROPTEXT, SDL_ENABLE);
+
     for (i = 0; i < state->num_windows; ++i) {
         SDL_Renderer *renderer = state->renderers[i];
         SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
diff --git a/source/wayland-protocols/pointer-constraints-unstable-v1.xml b/source/wayland-protocols/pointer-constraints-unstable-v1.xml
new file mode 100644
index 0000000..4e67a13
--- /dev/null
+++ b/source/wayland-protocols/pointer-constraints-unstable-v1.xml
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="pointer_constraints_unstable_v1">
+
+  <copyright>
+    Copyright © 2014      Jonas Ådahl
+    Copyright © 2015      Red Hat Inc.
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <description summary="protocol for constraining pointer motions">
+    This protocol specifies a set of interfaces used for adding constraints to
+    the motion of a pointer. Possible constraints include confining pointer
+    motions to a given region, or locking it to its current position.
+
+    In order to constrain the pointer, a client must first bind the global
+    interface "wp_pointer_constraints" which, if a compositor supports pointer
+    constraints, is exposed by the registry. Using the bound global object, the
+    client uses the request that corresponds to the type of constraint it wants
+    to make. See wp_pointer_constraints for more details.
+
+    Warning! The protocol described in this file is experimental and backward
+    incompatible changes may be made. Backward compatible changes may be added
+    together with the corresponding interface version bump. Backward
+    incompatible changes are done by bumping the version number in the protocol
+    and interface names and resetting the interface version. Once the protocol
+    is to be declared stable, the 'z' prefix and the version number in the
+    protocol and interface names are removed and the interface version number is
+    reset.
+  </description>
+
+  <interface name="zwp_pointer_constraints_v1" version="1">
+    <description summary="constrain the movement of a pointer">
+      The global interface exposing pointer constraining functionality. It
+      exposes two requests: lock_pointer for locking the pointer to its
+      position, and confine_pointer for locking the pointer to a region.
+
+      The lock_pointer and confine_pointer requests create the objects
+      wp_locked_pointer and wp_confined_pointer respectively, and the client can
+      use these objects to interact with the lock.
+
+      For any surface, only one lock or confinement may be active across all
+      wl_pointer objects of the same seat. If a lock or confinement is requested
+      when another lock or confinement is active or requested on the same surface
+      and with any of the wl_pointer objects of the same seat, an
+      'already_constrained' error will be raised.
+    </description>
+
+    <enum name="error">
+      <description summary="wp_pointer_constraints error values">
+	These errors can be emitted in response to wp_pointer_constraints
+	requests.
+      </description>
+      <entry name="already_constrained" value="1"
+	     summary="pointer constraint already requested on that surface"/>
+    </enum>
+
+    <enum name="lifetime">
+      <description summary="constraint lifetime">
+	These values represent different lifetime semantics. They are passed
+	as arguments to the factory requests to specify how the constraint
+	lifetimes should be managed.
+      </description>
+      <entry name="oneshot" value="1">
+	<description summary="the pointer constraint is defunct once deactivated">
+	  A oneshot pointer constraint will never reactivate once it has been
+	  deactivated. See the corresponding deactivation event
+	  (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
+	  details.
+	</description>
+      </entry>
+      <entry name="persistent" value="2">
+	<description summary="the pointer constraint may reactivate">
+	  A persistent pointer constraint may again reactivate once it has
+	  been deactivated. See the corresponding deactivation event
+	  (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
+	  details.
+	</description>
+      </entry>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the pointer constraints manager object">
+	Used by the client to notify the server that it will no longer use this
+	pointer constraints object.
+      </description>
+    </request>
+
+    <request name="lock_pointer">
+      <description summary="lock pointer to a position">
+	The lock_pointer request lets the client request to disable movements of
+	the virtual pointer (i.e. the cursor), effectively locking the pointer
+	to a position. This request may not take effect immediately; in the
+	future, when the compositor deems implementation-specific constraints
+	are satisfied, the pointer lock will be activated and the compositor
+	sends a locked event.
+
+	The protocol provides no guarantee that the constraints are ever
+	satisfied, and does not require the compositor to send an error if the
+	constraints cannot ever be satisfied. It is thus possible to request a
+	lock that will never activate.
+
+	There may not be another pointer constraint of any kind requested or
+	active on the surface for any of the wl_pointer objects of the seat of
+	the passed pointer when requesting a lock. If there is, an error will be
+	raised. See general pointer lock documentation for more details.
+
+	The intersection of the region passed with this request and the input
+	region of the surface is used to determine where the pointer must be
+	in order for the lock to activate. It is up to the compositor whether to
+	warp the pointer or require some kind of user interaction for the lock
+	to activate. If the region is null the surface input region is used.
+
+	A surface may receive pointer focus without the lock being activated.
+
+	The request creates a new object wp_locked_pointer which is used to
+	interact with the lock as well as receive updates about its state. See
+	the the description of wp_locked_pointer for further information.
+
+	Note that while a pointer is locked, the wl_pointer objects of the
+	corresponding seat will not emit any wl_pointer.motion events, but
+	relative motion events will still be emitted via wp_relative_pointer
+	objects of the same seat. wl_pointer.axis and wl_pointer.button events
+	are unaffected.
+      </description>
+      <arg name="id" type="new_id" interface="zwp_locked_pointer_v1"/>
+      <arg name="surface" type="object" interface="wl_surface"
+	   summary="surface to lock pointer to"/>
+      <arg name="pointer" type="object" interface="wl_pointer"
+	   summary="the pointer that should be locked"/>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+	   summary="region of surface"/>
+      <arg name="lifetime" type="uint" summary="lock lifetime"/>
+    </request>
+
+    <request name="confine_pointer">
+      <description summary="confine pointer to a region">
+	The confine_pointer request lets the client request to confine the
+	pointer cursor to a given region. This request may not take effect
+	immediately; in the future, when the compositor deems implementation-
+	specific constraints are satisfied, the pointer confinement will be
+	activated and the compositor sends a confined event.
+
+	The intersection of the region passed with this request and the input
+	region of the surface is used to determine where the pointer must be
+	in order for the confinement to activate. It is up to the compositor
+	whether to warp the pointer or require some kind of user interaction for
+	the confinement to activate. If the region is null the surface input
+	region is used.
+
+	The request will create a new object wp_confined_pointer which is used
+	to interact with the confinement as well as receive updates about its
+	state. See the the description of wp_confined_pointer for further
+	information.
+      </description>
+      <arg name="id" type="new_id" interface="zwp_confined_pointer_v1"/>
+      <arg name="surface" type="object" interface="wl_surface"
+	   summary="surface to lock pointer to"/>
+      <arg name="pointer" type="object" interface="wl_pointer"
+	   summary="the pointer that should be confined"/>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+	   summary="region of surface"/>
+      <arg name="lifetime" type="uint" summary="confinement lifetime"/>
+    </request>
+  </interface>
+
+  <interface name="zwp_locked_pointer_v1" version="1">
+    <description summary="receive relative pointer motion events">
+      The wp_locked_pointer interface represents a locked pointer state.
+
+      While the lock of this object is active, the wl_pointer objects of the
+      associated seat will not emit any wl_pointer.motion events.
+
+      This object will send the event 'locked' when the lock is activated.
+      Whenever the lock is activated, it is guaranteed that the locked surface
+      will already have received pointer focus and that the pointer will be
+      within the region passed to the request creating this object.
+
+      To unlock the pointer, send the destroy request. This will also destroy
+      the wp_locked_pointer object.
+
+      If the compositor decides to unlock the pointer the unlocked event is
+      sent. See wp_locked_pointer.unlock for details.
+
+      When unlocking, the compositor may warp the cursor position to the set
+      cursor position hint. If it does, it will not result in any relative
+      motion events emitted via wp_relative_pointer.
+
+      If the surface the lock was requested on is destroyed and the lock is not
+      yet activated, the wp_locked_pointer object is now defunct and must be
+      destroyed.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the locked pointer object">
+	Destroy the locked pointer object. If applicable, the compositor will
+	unlock the pointer.
+      </description>
+    </request>
+
+    <request name="set_cursor_position_hint">
+      <description summary="set the pointer cursor position hint">
+	Set the cursor position hint relative to the top left corner of the
+	surface.
+
+	If the client is drawing its own cursor, it should update the position
+	hint to the position of its own cursor. A compositor may use this
+	information to warp the pointer upon unlock in order to avoid pointer
+	jumps.
+
+	The cursor position hint is double buffered. The new hint will only take
+	effect when the associated surface gets it pending state applied. See
+	wl_surface.commit for details.
+      </description>
+      <arg name="surface_x" type="fixed"
+	   summary="surface-local x coordinate"/>
+      <arg name="surface_y" type="fixed"
+	   summary="surface-local y coordinate"/>
+    </request>
+
+    <request name="set_region">
+      <description summary="set a new lock region">
+	Set a new region used to lock the pointer.
+
+	The new lock region is double-buffered. The new lock region will
+	only take effect when the associated surface gets its pending state
+	applied. See wl_surface.commit for details.
+
+	For details about the lock region, see wp_locked_pointer.
+      </description>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+	   summary="region of surface"/>
+    </request>
+
+    <event name="locked">
+      <description summary="lock activation event">
+	Notification that the pointer lock of the seat's pointer is activated.
+      </description>
+    </event>
+
+    <event name="unlocked">
+      <description summary="lock deactivation event">
+	Notification that the pointer lock of the seat's pointer is no longer
+	active. If this is a oneshot pointer lock (see
+	wp_pointer_constraints.lifetime) this object is now defunct and should
+	be destroyed. If this is a persistent pointer lock (see
+	wp_pointer_constraints.lifetime) this pointer lock may again
+	reactivate in the future.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="zwp_confined_pointer_v1" version="1">
+    <description summary="confined pointer object">
+      The wp_confined_pointer interface represents a confined pointer state.
+
+      This object will send the event 'confined' when the confinement is
+      activated. Whenever the confinement is activated, it is guaranteed that
+      the surface the pointer is confined to will already have received pointer
+      focus and that the pointer will be within the region passed to the request
+      creating this object. It is up to the compositor to decide whether this
+      requires some user interaction and if the pointer will warp to within the
+      passed region if outside.
+
+      To unconfine the pointer, send the destroy request. This will also destroy
+      the wp_confined_pointer object.
+
+      If the compositor decides to unconfine the pointer the unconfined event is
+      sent. The wp_confined_pointer object is at this point defunct and should
+      be destroyed.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the confined pointer object">
+	Destroy the confined pointer object. If applicable, the compositor will
+	unconfine the pointer.
+      </description>
+    </request>
+
+    <request name="set_region">
+      <description summary="set a new confine region">
+	Set a new region used to confine the pointer.
+
+	The new confine region is double-buffered. The new confine region will
+	only take effect when the associated surface gets its pending state
+	applied. See wl_surface.commit for details.
+
+	If the confinement is active when the new confinement region is applied
+	and the pointer ends up outside of newly applied region, the pointer may
+	warped to a position within the new confinement region. If warped, a
+	wl_pointer.motion event will be emitted, but no
+	wp_relative_pointer.relative_motion event.
+
+	The compositor may also, instead of using the new region, unconfine the
+	pointer.
+
+	For details about the confine region, see wp_confined_pointer.
+      </description>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+	   summary="region of surface"/>
+    </request>
+
+    <event name="confined">
+      <description summary="pointer confined">
+	Notification that the pointer confinement of the seat's pointer is
+	activated.
+      </description>
+    </event>
+
+    <event name="unconfined">
+      <description summary="pointer unconfined">
+	Notification that the pointer confinement of the seat's pointer is no
+	longer active. If this is a oneshot pointer confinement (see
+	wp_pointer_constraints.lifetime) this object is now defunct and should
+	be destroyed. If this is a persistent pointer confinement (see
+	wp_pointer_constraints.lifetime) this pointer confinement may again
+	reactivate in the future.
+      </description>
+    </event>
+  </interface>
+
+</protocol>
diff --git a/source/wayland-protocols/relative-pointer-unstable-v1.xml b/source/wayland-protocols/relative-pointer-unstable-v1.xml
new file mode 100644
index 0000000..ca6f81d
--- /dev/null
+++ b/source/wayland-protocols/relative-pointer-unstable-v1.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="relative_pointer_unstable_v1">
+
+  <copyright>
+    Copyright © 2014      Jonas Ådahl
+    Copyright © 2015      Red Hat Inc.
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <description summary="protocol for relative pointer motion events">
+    This protocol specifies a set of interfaces used for making clients able to
+    receive relative pointer events not obstructed by barriers (such as the
+    monitor edge or other pointer barriers).
+
+    To start receiving relative pointer events, a client must first bind the
+    global interface "wp_relative_pointer_manager" which, if a compositor
+    supports relative pointer motion events, is exposed by the registry. After
+    having created the relative pointer manager proxy object, the client uses
+    it to create the actual relative pointer object using the
+    "get_relative_pointer" request given a wl_pointer. The relative pointer
+    motion events will then, when applicable, be transmitted via the proxy of
+    the newly created relative pointer object. See the documentation of the
+    relative pointer interface for more details.
+
+    Warning! The protocol described in this file is experimental and backward
+    incompatible changes may be made. Backward compatible changes may be added
+    together with the corresponding interface version bump. Backward
+    incompatible changes are done by bumping the version number in the protocol
+    and interface names and resetting the interface version. Once the protocol
+    is to be declared stable, the 'z' prefix and the version number in the
+    protocol and interface names are removed and the interface version number is
+    reset.
+  </description>
+
+  <interface name="zwp_relative_pointer_manager_v1" version="1">
+    <description summary="get relative pointer objects">
+      A global interface used for getting the relative pointer object for a
+      given pointer.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the relative pointer manager object">
+	Used by the client to notify the server that it will no longer use this
+	relative pointer manager object.
+      </description>
+    </request>
+
+    <request name="get_relative_pointer">
+      <description summary="get a relative pointer object">
+	Create a relative pointer interface given a wl_pointer object. See the
+	wp_relative_pointer interface for more details.
+      </description>
+      <arg name="id" type="new_id" interface="zwp_relative_pointer_v1"/>
+      <arg name="pointer" type="object" interface="wl_pointer"/>
+    </request>
+  </interface>
+
+  <interface name="zwp_relative_pointer_v1" version="1">
+    <description summary="relative pointer object">
+      A wp_relative_pointer object is an extension to the wl_pointer interface
+      used for emitting relative pointer events. It shares the same focus as
+      wl_pointer objects of the same seat and will only emit events when it has
+      focus.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="release the relative pointer object"/>
+    </request>
+
+    <event name="relative_motion">
+      <description summary="relative pointer motion">
+	Relative x/y pointer motion from the pointer of the seat associated with
+	this object.
+
+	A relative motion is in the same dimension as regular wl_pointer motion
+	events, except they do not represent an absolute position. For example,
+	moving a pointer from (x, y) to (x', y') would have the equivalent
+	relative motion (x' - x, y' - y). If a pointer motion caused the
+	absolute pointer position to be clipped by for example the edge of the
+	monitor, the relative motion is unaffected by the clipping and will
+	represent the unclipped motion.
+
+	This event also contains non-accelerated motion deltas. The
+	non-accelerated delta is, when applicable, the regular pointer motion
+	delta as it was before having applied motion acceleration and other
+	transformations such as normalization.
+
+	Note that the non-accelerated delta does not represent 'raw' events as
+	they were read from some device. Pointer motion acceleration is device-
+	and configuration-specific and non-accelerated deltas and accelerated
+	deltas may have the same value on some devices.
+
+	Relative motions are not coupled to wl_pointer.motion events, and can be
+	sent in combination with such events, but also independently. There may
+	also be scenarios where wl_pointer.motion is sent, but there is no
+	relative motion. The order of an absolute and relative motion event
+	originating from the same physical motion is not guaranteed.
+
+	If the client needs button events or focus state, it can receive them
+	from a wl_pointer object of the same seat that the wp_relative_pointer
+	object is associated with.
+      </description>
+      <arg name="utime_hi" type="uint"
+	   summary="high 32 bits of a 64 bit timestamp with microsecond granularity"/>
+      <arg name="utime_lo" type="uint"
+	   summary="low 32 bits of a 64 bit timestamp with microsecond granularity"/>
+      <arg name="dx" type="fixed"
+	   summary="the x component of the motion vector"/>
+      <arg name="dy" type="fixed"
+	   summary="the y component of the motion vector"/>
+      <arg name="dx_unaccel" type="fixed"
+	   summary="the x component of the unaccelerated motion vector"/>
+      <arg name="dy_unaccel" type="fixed"
+	   summary="the y component of the unaccelerated motion vector"/>
+    </event>
+  </interface>
+
+</protocol>
diff --git a/source/wayland-protocols/wayland.xml b/source/wayland-protocols/wayland.xml
new file mode 100644
index 0000000..29b63be
--- /dev/null
+++ b/source/wayland-protocols/wayland.xml
@@ -0,0 +1,2746 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wayland">
+
+  <copyright>
+    Copyright © 2008-2011 Kristian Høgsberg
+    Copyright © 2010-2011 Intel Corporation
+    Copyright © 2012-2013 Collabora, Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the
+    next paragraph) shall be included in all copies or substantial
+    portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+  </copyright>
+
+  <interface name="wl_display" version="1">
+    <description summary="core global object">
+      The core global object.  This is a special singleton object.  It
+      is used for internal Wayland protocol features.
+    </description>
+
+    <request name="sync">
+      <description summary="asynchronous roundtrip">
+	The sync request asks the server to emit the 'done' event
+	on the returned wl_callback object.  Since requests are
+	handled in-order and events are delivered in-order, this can
+	be used as a barrier to ensure all previous requests and the
+	resulting events have been handled.
+
+	The object returned by this request will be destroyed by the
+	compositor after the callback is fired and as such the client must not
+	attempt to use it after that point.
+
+	The callback_data passed in the callback is the event serial.
+      </description>
+      <arg name="callback" type="new_id" interface="wl_callback"
+	   summary="callback object for the sync request"/>
+    </request>
+
+    <request name="get_registry">
+      <description summary="get global registry object">
+	This request creates a registry object that allows the client
+	to list and bind the global objects available from the
+	compositor.
+      </description>
+      <arg name="registry" type="new_id" interface="wl_registry"
+	   summary="global registry object"/>
+    </request>
+
+    <event name="error">
+      <description summary="fatal error event">
+	The error event is sent out when a fatal (non-recoverable)
+	error has occurred.  The object_id argument is the object
+	where the error occurred, most often in response to a request
+	to that object.  The code identifies the error and is defined
+	by the object interface.  As such, each interface defines its
+	own set of error codes.  The message is a brief description
+	of the error, for (debugging) convenience.
+      </description>
+      <arg name="object_id" type="object" summary="object where the error occurred"/>
+      <arg name="code" type="uint" summary="error code"/>
+      <arg name="message" type="string" summary="error description"/>
+    </event>
+
+    <enum name="error">
+      <description summary="global error values">
+	These errors are global and can be emitted in response to any
+	server request.
+      </description>
+      <entry name="invalid_object" value="0"
+	     summary="server couldn't find object"/>
+      <entry name="invalid_method" value="1"
+	     summary="method doesn't exist on the specified interface"/>
+      <entry name="no_memory" value="2"
+	     summary="server is out of memory"/>
+    </enum>
+
+    <event name="delete_id">
+      <description summary="acknowledge object ID deletion">
+	This event is used internally by the object ID management
+	logic.  When a client deletes an object, the server will send
+	this event to acknowledge that it has seen the delete request.
+	When the client receives this event, it will know that it can
+	safely reuse the object ID.
+      </description>
+      <arg name="id" type="uint" summary="deleted object ID"/>
+    </event>
+  </interface>
+
+  <interface name="wl_registry" version="1">
+    <description summary="global registry object">
+      The singleton global registry object.  The server has a number of
+      global objects that are available to all clients.  These objects
+      typically represent an actual object in the server (for example,
+      an input device) or they are singleton objects that provide
+      extension functionality.
+
+      When a client creates a registry object, the registry object
+      will emit a global event for each global currently in the
+      registry.  Globals come and go as a result of device or
+      monitor hotplugs, reconfiguration or other events, and the
+      registry will send out global and global_remove events to
+      keep the client up to date with the changes.  To mark the end
+      of the initial burst of events, the client can use the
+      wl_display.sync request immediately after calling
+      wl_display.get_registry.
+
+      A client can bind to a global object by using the bind
+      request.  This creates a client-side handle that lets the object
+      emit events to the client and lets the client invoke requests on
+      the object.
+    </description>
+
+    <request name="bind">
+      <description summary="bind an object to the display">
+	Binds a new, client-created object to the server using the
+	specified name as the identifier.
+      </description>
+      <arg name="name" type="uint" summary="unique numeric name of the object"/>
+      <arg name="id" type="new_id" summary="bounded object"/>
+    </request>
+
+    <event name="global">
+      <description summary="announce global object">
+	Notify the client of global objects.
+
+	The event notifies the client that a global object with
+	the given name is now available, and it implements the
+	given version of the given interface.
+      </description>
+      <arg name="name" type="uint" summary="numeric name of the global object"/>
+      <arg name="interface" type="string" summary="interface implemented by the object"/>
+      <arg name="version" type="uint" summary="interface version"/>
+    </event>
+
+    <event name="global_remove">
+      <description summary="announce removal of global object">
+	Notify the client of removed global objects.
+
+	This event notifies the client that the global identified
+	by name is no longer available.  If the client bound to
+	the global using the bind request, the client should now
+	destroy that object.
+
+	The object remains valid and requests to the object will be
+	ignored until the client destroys it, to avoid races between
+	the global going away and a client sending a request to it.
+      </description>
+      <arg name="name" type="uint" summary="numeric name of the global object"/>
+    </event>
+  </interface>
+
+  <interface name="wl_callback" version="1">
+    <description summary="callback object">
+      Clients can handle the 'done' event to get notified when
+      the related request is done.
+    </description>
+
+    <event name="done">
+      <description summary="done event">
+	Notify the client when the related request is done.
+      </description>
+      <arg name="callback_data" type="uint" summary="request-specific data for the callback"/>
+    </event>
+  </interface>
+
+  <interface name="wl_compositor" version="4">
+    <description summary="the compositor singleton">
+      A compositor.  This object is a singleton global.  The
+      compositor is in charge of combining the contents of multiple
+      surfaces into one displayable output.
+    </description>
+
+    <request name="create_surface">
+      <description summary="create new surface">
+	Ask the compositor to create a new surface.
+      </description>
+      <arg name="id" type="new_id" interface="wl_surface" summary="the new surface"/>
+    </request>
+
+    <request name="create_region">
+      <description summary="create new region">
+	Ask the compositor to create a new region.
+      </description>
+      <arg name="id" type="new_id" interface="wl_region" summary="the new region"/>
+    </request>
+  </interface>
+
+  <interface name="wl_shm_pool" version="1">
+    <description summary="a shared memory pool">
+      The wl_shm_pool object encapsulates a piece of memory shared
+      between the compositor and client.  Through the wl_shm_pool
+      object, the client can allocate shared memory wl_buffer objects.
+      All objects created through the same pool share the same
+      underlying mapped memory. Reusing the mapped memory avoids the
+      setup/teardown overhead and is useful when interactively resizing
+      a surface or for many small buffers.
+    </description>
+
+    <request name="create_buffer">
+      <description summary="create a buffer from the pool">
+	Create a wl_buffer object from the pool.
+
+	The buffer is created offset bytes into the pool and has
+	width and height as specified.  The stride argument specifies
+	the number of bytes from the beginning of one row to the beginning
+	of the next.  The format is the pixel format of the buffer and
+	must be one of those advertised through the wl_shm.format event.
+
+	A buffer will keep a reference to the pool it was created from
+	so it is valid to destroy the pool immediately after creating
+	a buffer from it.
+      </description>
+      <arg name="id" type="new_id" interface="wl_buffer" summary="buffer to create"/>
+      <arg name="offset" type="int" summary="buffer byte offset within the pool"/>
+      <arg name="width" type="int" summary="buffer width, in pixels"/>
+      <arg name="height" type="int" summary="buffer height, in pixels"/>
+      <arg name="stride" type="int" summary="number of bytes from the beginning of one row to the beginning of the next row"/>
+      <arg name="format" type="uint" enum="wl_shm.format" summary="buffer pixel format"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the pool">
+	Destroy the shared memory pool.
+
+	The mmapped memory will be released when all
+	buffers that have been created from this pool
+	are gone.
+      </description>
+    </request>
+
+    <request name="resize">
+      <description summary="change the size of the pool mapping">
+	This request will cause the server to remap the backing memory
+	for the pool from the file descriptor passed when the pool was
+	created, but using the new size.  This request can only be
+	used to make the pool bigger.
+      </description>
+      <arg name="size" type="int" summary="new size of the pool, in bytes"/>
+    </request>
+  </interface>
+
+  <interface name="wl_shm" version="1">
+    <description summary="shared memory support">
+      A singleton global object that provides support for shared
+      memory.
+
+      Clients can create wl_shm_pool objects using the create_pool
+      request.
+
+      At connection setup time, the wl_shm object emits one or more
+      format events to inform clients about the valid pixel formats
+      that can be used for buffers.
+    </description>
+
+    <enum name="error">
+      <description summary="wl_shm error values">
+	These errors can be emitted in response to wl_shm requests.
+      </description>
+      <entry name="invalid_format" value="0" summary="buffer format is not known"/>
+      <entry name="invalid_stride" value="1" summary="invalid size or stride during pool or buffer creation"/>
+      <entry name="invalid_fd" value="2" summary="mmapping the file descriptor failed"/>
+    </enum>
+
+    <enum name="format">
+      <description summary="pixel formats">
+	This describes the memory layout of an individual pixel.
+
+	All renderers should support argb8888 and xrgb8888 but any other
+	formats are optional and may not be supported by the particular
+	renderer in use.
+
+	The drm format codes match the macros defined in drm_fourcc.h.
+	The formats actually supported by the compositor will be
+	reported by the format event.
+      </description>
+      <entry name="argb8888" value="0" summary="32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian"/>
+      <entry name="xrgb8888" value="1" summary="32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian"/>
+      <entry name="c8" value="0x20203843" summary="8-bit color index format, [7:0] C"/>
+      <entry name="rgb332" value="0x38424752" summary="8-bit RGB format, [7:0] R:G:B 3:3:2"/>
+      <entry name="bgr233" value="0x38524742" summary="8-bit BGR format, [7:0] B:G:R 2:3:3"/>
+      <entry name="xrgb4444" value="0x32315258" summary="16-bit xRGB format, [15:0] x:R:G:B 4:4:4:4 little endian"/>
+      <entry name="xbgr4444" value="0x32314258" summary="16-bit xBGR format, [15:0] x:B:G:R 4:4:4:4 little endian"/>
+      <entry name="rgbx4444" value="0x32315852" summary="16-bit RGBx format, [15:0] R:G:B:x 4:4:4:4 little endian"/>
+      <entry name="bgrx4444" value="0x32315842" summary="16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian"/>
+      <entry name="argb4444" value="0x32315241" summary="16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian"/>
+      <entry name="abgr4444" value="0x32314241" summary="16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian"/>
+      <entry name="rgba4444" value="0x32314152" summary="16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian"/>
+      <entry name="bgra4444" value="0x32314142" summary="16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian"/>
+      <entry name="xrgb1555" value="0x35315258" summary="16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian"/>
+      <entry name="xbgr1555" value="0x35314258" summary="16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian"/>
+      <entry name="rgbx5551" value="0x35315852" summary="16-bit RGBx 5551 format, [15:0] R:G:B:x 5:5:5:1 little endian"/>
+      <entry name="bgrx5551" value="0x35315842" summary="16-bit BGRx 5551 format, [15:0] B:G:R:x 5:5:5:1 little endian"/>
+      <entry name="argb1555" value="0x35315241" summary="16-bit ARGB 1555 format, [15:0] A:R:G:B 1:5:5:5 little endian"/>
+      <entry name="abgr1555" value="0x35314241" summary="16-bit ABGR 1555 format, [15:0] A:B:G:R 1:5:5:5 little endian"/>
+      <entry name="rgba5551" value="0x35314152" summary="16-bit RGBA 5551 format, [15:0] R:G:B:A 5:5:5:1 little endian"/>
+      <entry name="bgra5551" value="0x35314142" summary="16-bit BGRA 5551 format, [15:0] B:G:R:A 5:5:5:1 little endian"/>
+      <entry name="rgb565" value="0x36314752" summary="16-bit RGB 565 format, [15:0] R:G:B 5:6:5 little endian"/>
+      <entry name="bgr565" value="0x36314742" summary="16-bit BGR 565 format, [15:0] B:G:R 5:6:5 little endian"/>
+      <entry name="rgb888" value="0x34324752" summary="24-bit RGB format, [23:0] R:G:B little endian"/>
+      <entry name="bgr888" value="0x34324742" summary="24-bit BGR format, [23:0] B:G:R little endian"/>
+      <entry name="xbgr8888" value="0x34324258" summary="32-bit xBGR format, [31:0] x:B:G:R 8:8:8:8 little endian"/>
+      <entry name="rgbx8888" value="0x34325852" summary="32-bit RGBx format, [31:0] R:G:B:x 8:8:8:8 little endian"/>
+      <entry name="bgrx8888" value="0x34325842" summary="32-bit BGRx format, [31:0] B:G:R:x 8:8:8:8 little endian"/>
+      <entry name="abgr8888" value="0x34324241" summary="32-bit ABGR format, [31:0] A:B:G:R 8:8:8:8 little endian"/>
+      <entry name="rgba8888" value="0x34324152" summary="32-bit RGBA format, [31:0] R:G:B:A 8:8:8:8 little endian"/>
+      <entry name="bgra8888" value="0x34324142" summary="32-bit BGRA format, [31:0] B:G:R:A 8:8:8:8 little endian"/>
+      <entry name="xrgb2101010" value="0x30335258" summary="32-bit xRGB format, [31:0] x:R:G:B 2:10:10:10 little endian"/>
+      <entry name="xbgr2101010" value="0x30334258" summary="32-bit xBGR format, [31:0] x:B:G:R 2:10:10:10 little endian"/>
+      <entry name="rgbx1010102" value="0x30335852" summary="32-bit RGBx format, [31:0] R:G:B:x 10:10:10:2 little endian"/>
+      <entry name="bgrx1010102" value="0x30335842" summary="32-bit BGRx format, [31:0] B:G:R:x 10:10:10:2 little endian"/>
+      <entry name="argb2101010" value="0x30335241" summary="32-bit ARGB format, [31:0] A:R:G:B 2:10:10:10 little endian"/>
+      <entry name="abgr2101010" value="0x30334241" summary="32-bit ABGR format, [31:0] A:B:G:R 2:10:10:10 little endian"/>
+      <entry name="rgba1010102" value="0x30334152" summary="32-bit RGBA format, [31:0] R:G:B:A 10:10:10:2 little endian"/>
+      <entry name="bgra1010102" value="0x30334142" summary="32-bit BGRA format, [31:0] B:G:R:A 10:10:10:2 little endian"/>
+      <entry name="yuyv" value="0x56595559" summary="packed YCbCr format, [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian"/>
+      <entry name="yvyu" value="0x55595659" summary="packed YCbCr format, [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian"/>
+      <entry name="uyvy" value="0x59565955" summary="packed YCbCr format, [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian"/>
+      <entry name="vyuy" value="0x59555956" summary="packed YCbCr format, [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian"/>
+      <entry name="ayuv" value="0x56555941" summary="packed AYCbCr format, [31:0] A:Y:Cb:Cr 8:8:8:8 little endian"/>
+      <entry name="nv12" value="0x3231564e" summary="2 plane YCbCr Cr:Cb format, 2x2 subsampled Cr:Cb plane"/>
+      <entry name="nv21" value="0x3132564e" summary="2 plane YCbCr Cb:Cr format, 2x2 subsampled Cb:Cr plane"/>
+      <entry name="nv16" value="0x3631564e" summary="2 plane YCbCr Cr:Cb format, 2x1 subsampled Cr:Cb plane"/>
+      <entry name="nv61" value="0x3136564e" summary="2 plane YCbCr Cb:Cr format, 2x1 subsampled Cb:Cr plane"/>
+      <entry name="yuv410" value="0x39565559" summary="3 plane YCbCr format, 4x4 subsampled Cb (1) and Cr (2) planes"/>
+      <entry name="yvu410" value="0x39555659" summary="3 plane YCbCr format, 4x4 subsampled Cr (1) and Cb (2) planes"/>
+      <entry name="yuv411" value="0x31315559" summary="3 plane YCbCr format, 4x1 subsampled Cb (1) and Cr (2) planes"/>
+      <entry name="yvu411" value="0x31315659" summary="3 plane YCbCr format, 4x1 subsampled Cr (1) and Cb (2) planes"/>
+      <entry name="yuv420" value="0x32315559" summary="3 plane YCbCr format, 2x2 subsampled Cb (1) and Cr (2) planes"/>
+      <entry name="yvu420" value="0x32315659" summary="3 plane YCbCr format, 2x2 subsampled Cr (1) and Cb (2) planes"/>
+      <entry name="yuv422" value="0x36315559" summary="3 plane YCbCr format, 2x1 subsampled Cb (1) and Cr (2) planes"/>
+      <entry name="yvu422" value="0x36315659" summary="3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes"/>
+      <entry name="yuv444" value="0x34325559" summary="3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes"/>
+      <entry name="yvu444" value="0x34325659" summary="3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes"/>
+    </enum>
+
+    <request name="create_pool">
+      <description summary="create a shm pool">
+	Create a new wl_shm_pool object.
+
+	The pool can be used to create shared memory based buffer
+	objects.  The server will mmap size bytes of the passed file
+	descriptor, to use as backing memory for the pool.
+      </description>
+      <arg name="id" type="new_id" interface="wl_shm_pool" summary="pool to create"/>
+      <arg name="fd" type="fd" summary="file descriptor for the pool"/>
+      <arg name="size" type="int" summary="pool size, in bytes"/>
+    </request>
+
+    <event name="format">
+      <description summary="pixel format description">
+	Informs the client about a valid pixel format that
+	can be used for buffers. Known formats include
+	argb8888 and xrgb8888.
+      </description>
+      <arg name="format" type="uint" enum="format" summary="buffer pixel format"/>
+    </event>
+  </interface>
+
+  <interface name="wl_buffer" version="1">
+    <description summary="content for a wl_surface">
+      A buffer provides the content for a wl_surface. Buffers are
+      created through factory interfaces such as wl_drm, wl_shm or
+      similar. It has a width and a height and can be attached to a
+      wl_surface, but the mechanism by which a client provides and
+      updates the contents is defined by the buffer factory interface.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy a buffer">
+	Destroy a buffer. If and how you need to release the backing
+	storage is defined by the buffer factory interface.
+
+	For possible side-effects to a surface, see wl_surface.attach.
+      </description>
+    </request>
+
+    <event name="release">
+      <description summary="compositor releases buffer">
+	Sent when this wl_buffer is no longer used by the compositor.
+	The client is now free to reuse or destroy this buffer and its
+	backing storage.
+
+	If a client receives a release event before the frame callback
+	requested in the same wl_surface.commit that attaches this
+	wl_buffer to a surface, then the client is immediately free to
+	reuse the buffer and its backing storage, and does not need a
+	second buffer for the next surface content update. Typically
+	this is possible, when the compositor maintains a copy of the
+	wl_surface contents, e.g. as a GL texture. This is an important
+	optimization for GL(ES) compositors with wl_shm clients.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="wl_data_offer" version="3">
+    <description summary="offer to transfer data">
+      A wl_data_offer represents a piece of data offered for transfer
+      by another client (the source client).  It is used by the
+      copy-and-paste and drag-and-drop mechanisms.  The offer
+      describes the different mime types that the data can be
+      converted to and provides the mechanism for transferring the
+      data directly from the source client.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_finish" value="0"
+	     summary="finish request was called untimely"/>
+      <entry name="invalid_action_mask" value="1"
+	     summary="action mask contains invalid values"/>
+      <entry name="invalid_action" value="2"
+	     summary="action argument has an invalid value"/>
+      <entry name="invalid_offer" value="3"
+	     summary="offer doesn't accept this request"/>
+    </enum>
+
+    <request name="accept">
+      <description summary="accept one of the offered mime types">
+	Indicate that the client can accept the given mime type, or
+	NULL for not accepted.
+
+	For objects of version 2 or older, this request is used by the
+	client to give feedback whether the client can receive the given
+	mime type, or NULL if none is accepted; the feedback does not
+	determine whether the drag-and-drop operation succeeds or not.
+
+	For objects of version 3 or newer, this request determines the
+	final result of the drag-and-drop operation. If the end result
+	is that no mime types were accepted, the drag-and-drop operation
+	will be cancelled and the corresponding drag source will receive
+	wl_data_source.cancelled. Clients may still use this event in
+	conjunction with wl_data_source.action for feedback.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the accept request"/>
+      <arg name="mime_type" type="string" allow-null="true" summary="mime type accepted by the client"/>
+    </request>
+
+    <request name="receive">
+      <description summary="request that the data is transferred">
+	To transfer the offered data, the client issues this request
+	and indicates the mime type it wants to receive.  The transfer
+	happens through the passed file descriptor (typically created
+	with the pipe system call).  The source client writes the data
+	in the mime type representation requested and then closes the
+	file descriptor.
+
+	The receiving client reads from the read end of the pipe until
+	EOF and then closes its end, at which point the transfer is
+	complete.
+
+	This request may happen multiple times for different mime types,
+	both before and after wl_data_device.drop. Drag-and-drop destination
+	clients may preemptively fetch data or examine it more closely to
+	determine acceptance.
+      </description>
+      <arg name="mime_type" type="string" summary="mime type desired by receiver"/>
+      <arg name="fd" type="fd" summary="file descriptor for data transfer"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy data offer">
+	Destroy the data offer.
+      </description>
+    </request>
+
+    <event name="offer">
+      <description summary="advertise offered mime type">
+	Sent immediately after creating the wl_data_offer object.  One
+	event per offered mime type.
+      </description>
+      <arg name="mime_type" type="string" summary="offered mime type"/>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="finish" since="3">
+      <description summary="the offer will no longer be used">
+	Notifies the compositor that the drag destination successfully
+	finished the drag-and-drop operation.
+
+	Upon receiving this request, the compositor will emit
+	wl_data_source.dnd_finished on the drag source client.
+
+	It is a client error to perform other requests than
+	wl_data_offer.destroy after this one. It is also an error to perform
+	this request after a NULL mime type has been set in
+	wl_data_offer.accept or no action was received through
+	wl_data_offer.action.
+      </description>
+    </request>
+
+    <request name="set_actions" since="3">
+      <description summary="set the available/preferred drag-and-drop actions">
+	Sets the actions that the destination side client supports for
+	this operation. This request may trigger the emission of
+	wl_data_source.action and wl_data_offer.action events if the compositor
+	needs to change the selected action.
+
+	This request can be called multiple times throughout the
+	drag-and-drop operation, typically in response to wl_data_device.enter
+	or wl_data_device.motion events.
+
+	This request determines the final result of the drag-and-drop
+	operation. If the end result is that no action is accepted,
+	the drag source will receive wl_drag_source.cancelled.
+
+	The dnd_actions argument must contain only values expressed in the
+	wl_data_device_manager.dnd_actions enum, and the preferred_action
+	argument must only contain one of those values set, otherwise it
+	will result in a protocol error.
+
+	While managing an "ask" action, the destination drag-and-drop client
+	may perform further wl_data_offer.receive requests, and is expected
+	to perform one last wl_data_offer.set_actions request with a preferred
+	action other than "ask" (and optionally wl_data_offer.accept) before
+	requesting wl_data_offer.finish, in order to convey the action selected
+	by the user. If the preferred action is not in the
+	wl_data_offer.source_actions mask, an error will be raised.
+
+	If the "ask" action is dismissed (e.g. user cancellation), the client
+	is expected to perform wl_data_offer.destroy right away.
+
+	This request can only be made on drag-and-drop offers, a protocol error
+	will be raised otherwise.
+      </description>
+      <arg name="dnd_actions" type="uint" summary="actions supported by the destination client"/>
+      <arg name="preferred_action" type="uint" summary="action preferred by the destination client"/>
+    </request>
+
+    <event name="source_actions" since="3">
+      <description summary="notify the source-side available actions">
+	This event indicates the actions offered by the data source. It
+	will be sent right after wl_data_device.enter, or anytime the source
+	side changes its offered actions through wl_data_source.set_actions.
+      </description>
+      <arg name="source_actions" type="uint" summary="actions offered by the data source"/>
+    </event>
+
+    <event name="action" since="3">
+      <description summary="notify the selected action">
+	This event indicates the action selected by the compositor after
+	matching the source/destination side actions. Only one action (or
+	none) will be offered here.
+
+	This event can be emitted multiple times during the drag-and-drop
+	operation in response to destination side action changes through
+	wl_data_offer.set_actions.
+
+	This event will no longer be emitted after wl_data_device.drop
+	happened on the drag-and-drop destination, the client must
+	honor the last action received, or the last preferred one set
+	through wl_data_offer.set_actions when handling an "ask" action.
+
+	Compositors may also change the selected action on the fly, mainly
+	in response to keyboard modifier changes during the drag-and-drop
+	operation.
+
+	The most recent action received is always the valid one. Prior to
+	receiving wl_data_device.drop, the chosen action may change (e.g.
+	due to keyboard modifiers being pressed). At the time of receiving
+	wl_data_device.drop the drag-and-drop destination must honor the
+	last action received.
+
+	Action changes may still happen after wl_data_device.drop,
+	especially on "ask" actions, where the drag-and-drop destination
+	may choose another action afterwards. Action changes happening
+	at this stage are always the result of inter-client negotiation, the
+	compositor shall no longer be able to induce a different action.
+
+	Upon "ask" actions, it is expected that the drag-and-drop destination
+	may potentially choose a different action and/or mime type,
+	based on wl_data_offer.source_actions and finally chosen by the
+	user (e.g. popping up a menu with the available options). The
+	final wl_data_offer.set_actions and wl_data_offer.accept requests
+	must happen before the call to wl_data_offer.finish.
+      </description>
+      <arg name="dnd_action" type="uint" summary="action selected by the compositor"/>
+    </event>
+  </interface>
+
+  <interface name="wl_data_source" version="3">
+    <description summary="offer to transfer data">
+      The wl_data_source object is the source side of a wl_data_offer.
+      It is created by the source client in a data transfer and
+      provides a way to describe the offered data and a way to respond
+      to requests to transfer the data.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_action_mask" value="0"
+	     summary="action mask contains invalid values"/>
+      <entry name="invalid_source" value="1"
+	     summary="source doesn't accept this request"/>
+    </enum>
+
+    <request name="offer">
+      <description summary="add an offered mime type">
+	This request adds a mime type to the set of mime types
+	advertised to targets.  Can be called several times to offer
+	multiple types.
+      </description>
+      <arg name="mime_type" type="string" summary="mime type offered by the data source"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the data source">
+	Destroy the data source.
+      </description>
+    </request>
+
+    <event name="target">
+      <description summary="a target accepts an offered mime type">
+	Sent when a target accepts pointer_focus or motion events.  If
+	a target does not accept any of the offered types, type is NULL.
+
+	Used for feedback during drag-and-drop.
+      </description>
+      <arg name="mime_type" type="string" allow-null="true" summary="mime type accepted by the target"/>
+    </event>
+
+    <event name="send">
+      <description summary="send the data">
+	Request for data from the client.  Send the data as the
+	specified mime type over the passed file descriptor, then
+	close it.
+      </description>
+      <arg name="mime_type" type="string" summary="mime type for the data"/>
+      <arg name="fd" type="fd" summary="file descriptor for the data"/>
+    </event>
+
+    <event name="cancelled">
+      <description summary="selection was cancelled">
+	This data source is no longer valid. There are several reasons why
+	this could happen:
+
+	- The data source has been replaced by another data source.
+	- The drag-and-drop operation was performed, but the drop destination
+	  did not accept any of the mime types offered through
+	  wl_data_source.target.
+	- The drag-and-drop operation was performed, but the drop destination
+	  did not select any of the actions present in the mask offered through
+	  wl_data_source.action.
+	- The drag-and-drop operation was performed but didn't happen over a
+	  surface.
+	- The compositor cancelled the drag-and-drop operation (e.g. compositor
+	  dependent timeouts to avoid stale drag-and-drop transfers).
+
+	The client should clean up and destroy this data source.
+
+	For objects of version 2 or older, wl_data_source.cancelled will
+	only be emitted if the data source was replaced by another data
+	source.
+      </description>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="set_actions" since="3">
+      <description summary="set the available drag-and-drop actions">
+	Sets the actions that the source side client supports for this
+	operation. This request may trigger wl_data_source.action and
+	wl_data_offer.action events if the compositor needs to change the
+	selected action.
+
+	The dnd_actions argument must contain only values expressed in the
+	wl_data_device_manager.dnd_actions enum, otherwise it will result
+	in a protocol error.
+
+	This request must be made once only, and can only be made on sources
+	used in drag-and-drop, so it must be performed before
+	wl_data_device.start_drag. Attempting to use the source other than
+	for drag-and-drop will raise a protocol error.
+      </description>
+      <arg name="dnd_actions" type="uint" summary="actions supported by the data source"/>
+    </request>
+
+    <event name="dnd_drop_performed" since="3">
+      <description summary="the drag-and-drop operation physically finished">
+	The user performed the drop action. This event does not indicate
+	acceptance, wl_data_source.cancelled may still be emitted afterwards
+	if the drop destination does not accept any mime type.
+
+	However, this event might however not be received if the compositor
+	cancelled the drag-and-drop operation before this event could happen.
+
+	Note that the data_source may still be used in the future and should
+	not be destroyed here.
+      </description>
+    </event>
+
+    <event name="dnd_finished" since="3">
+      <description summary="the drag-and-drop operation concluded">
+	The drop destination finished interoperating with this data
+	source, so the client is now free to destroy this data source and
+	free all associated data.
+
+	If the action used to perform the operation was "move", the
+	source can now delete the transferred data.
+      </description>
+    </event>
+
+    <event name="action" since="3">
+      <description summary="notify the selected action">
+	This event indicates the action selected by the compositor after
+	matching the source/destination side actions. Only one action (or
+	none) will be offered here.
+
+	This event can be emitted multiple times during the drag-and-drop
+	operation, mainly in response to destination side changes through
+	wl_data_offer.set_actions, and as the data device enters/leaves
+	surfaces.
+
+	It is only possible to receive this event after
+	wl_data_source.dnd_drop_performed if the drag-and-drop operation
+	ended in an "ask" action, in which case the final wl_data_source.action
+	event will happen immediately before wl_data_source.dnd_finished.
+
+	Compositors may also change the selected action on the fly, mainly
+	in response to keyboard modifier changes during the drag-and-drop
+	operation.
+
+	The most recent action received is always the valid one. The chosen
+	action may change alongside negotiation (e.g. an "ask" action can turn
+	into a "move" operation), so the effects of the final action must
+	always be applied in wl_data_offer.dnd_finished.
+
+	Clients can trigger cursor surface changes from this point, so
+	they reflect the current action.
+      </description>
+      <arg name="dnd_action" type="uint" summary="action selected by the compositor"/>
+    </event>
+  </interface>
+
+  <interface name="wl_data_device" version="3">
+    <description summary="data transfer device">
+      There is one wl_data_device per seat which can be obtained
+      from the global wl_data_device_manager singleton.
+
+      A wl_data_device provides access to inter-client data transfer
+      mechanisms such as copy-and-paste and drag-and-drop.
+    </description>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="given wl_surface has another role"/>
+    </enum>
+
+    <request name="start_drag">
+      <description summary="start drag-and-drop operation">
+	This request asks the compositor to start a drag-and-drop
+	operation on behalf of the client.
+
+	The source argument is the data source that provides the data
+	for the eventual data transfer. If source is NULL, enter, leave
+	and motion events are sent only to the client that initiated the
+	drag and the client is expected to handle the data passing
+	internally.
+
+	The origin surface is the surface where the drag originates and
+	the client must have an active implicit grab that matches the
+	serial.
+
+	The icon surface is an optional (can be NULL) surface that
+	provides an icon to be moved around with the cursor.  Initially,
+	the top-left corner of the icon surface is placed at the cursor
+	hotspot, but subsequent wl_surface.attach request can move the
+	relative position. Attach requests must be confirmed with
+	wl_surface.commit as usual. The icon surface is given the role of
+	a drag-and-drop icon. If the icon surface already has another role,
+	it raises a protocol error.
+
+	The current and pending input regions of the icon wl_surface are
+	cleared, and wl_surface.set_input_region is ignored until the
+	wl_surface is no longer used as the icon surface. When the use
+	as an icon ends, the current and pending input regions become
+	undefined, and the wl_surface is unmapped.
+      </description>
+      <arg name="source" type="object" interface="wl_data_source" allow-null="true" summary="data source for the eventual transfer"/>
+      <arg name="origin" type="object" interface="wl_surface" summary="surface where the drag originates"/>
+      <arg name="icon" type="object" interface="wl_surface" allow-null="true" summary="drag-and-drop icon surface"/>
+      <arg name="serial" type="uint" summary="serial number of the implicit grab on the origin"/>
+    </request>
+
+    <request name="set_selection">
+      <description summary="copy data to the selection">
+	This request asks the compositor to set the selection
+	to the data from the source on behalf of the client.
+
+	To unset the selection, set the source to NULL.
+      </description>
+      <arg name="source" type="object" interface="wl_data_source" allow-null="true" summary="data source for the selection"/>
+      <arg name="serial" type="uint" summary="serial number of the event that triggered this request"/>
+    </request>
+
+    <event name="data_offer">
+      <description summary="introduce a new wl_data_offer">
+	The data_offer event introduces a new wl_data_offer object,
+	which will subsequently be used in either the
+	data_device.enter event (for drag-and-drop) or the
+	data_device.selection event (for selections).  Immediately
+	following the data_device_data_offer event, the new data_offer
+	object will send out data_offer.offer events to describe the
+	mime types it offers.
+      </description>
+      <arg name="id" type="new_id" interface="wl_data_offer" summary="the new data_offer object"/>
+    </event>
+
+    <event name="enter">
+      <description summary="initiate drag-and-drop session">
+	This event is sent when an active drag-and-drop pointer enters
+	a surface owned by the client.  The position of the pointer at
+	enter time is provided by the x and y arguments, in surface-local
+	coordinates.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the enter event"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="client surface entered"/>
+      <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+      <arg name="y" type="fixed" summary="surface-local y coordinate"/>
+      <arg name="id" type="object" interface="wl_data_offer" allow-null="true"
+	   summary="source data_offer object"/>
+    </event>
+
+    <event name="leave">
+      <description summary="end drag-and-drop session">
+	This event is sent when the drag-and-drop pointer leaves the
+	surface and the session ends.  The client must destroy the
+	wl_data_offer introduced at enter time at this point.
+      </description>
+    </event>
+
+    <event name="motion">
+      <description summary="drag-and-drop session motion">
+	This event is sent when the drag-and-drop pointer moves within
+	the currently focused surface. The new position of the pointer
+	is provided by the x and y arguments, in surface-local
+	coordinates.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+      <arg name="y" type="fixed" summary="surface-local y coordinate"/>
+    </event>
+
+    <event name="drop">
+      <description summary="end drag-and-drop session successfully">
+	The event is sent when a drag-and-drop operation is ended
+	because the implicit grab is removed.
+
+	The drag-and-drop destination is expected to honor the last action
+	received through wl_data_offer.action, if the resulting action is
+	"copy" or "move", the destination can still perform
+	wl_data_offer.receive requests, and is expected to end all
+	transfers with a wl_data_offer.finish request.
+
+	If the resulting action is "ask", the action will not be considered
+	final. The drag-and-drop destination is expected to perform one last
+	wl_data_offer.set_actions request, or wl_data_offer.destroy in order
+	to cancel the operation.
+      </description>
+    </event>
+
+    <event name="selection">
+      <description summary="advertise new selection">
+	The selection event is sent out to notify the client of a new
+	wl_data_offer for the selection for this device.  The
+	data_device.data_offer and the data_offer.offer events are
+	sent out immediately before this event to introduce the data
+	offer object.  The selection event is sent to a client
+	immediately before receiving keyboard focus and when a new
+	selection is set while the client has keyboard focus.  The
+	data_offer is valid until a new data_offer or NULL is received
+	or until the client loses keyboard focus.  The client must
+	destroy the previous selection data_offer, if any, upon receiving
+	this event.
+      </description>
+      <arg name="id" type="object" interface="wl_data_offer" allow-null="true"
+	   summary="selection data_offer object"/>
+    </event>
+
+    <!-- Version 2 additions -->
+
+    <request name="release" type="destructor" since="2">
+      <description summary="destroy data device">
+	This request destroys the data device.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="wl_data_device_manager" version="3">
+    <description summary="data transfer interface">
+      The wl_data_device_manager is a singleton global object that
+      provides access to inter-client data transfer mechanisms such as
+      copy-and-paste and drag-and-drop.  These mechanisms are tied to
+      a wl_seat and this interface lets a client get a wl_data_device
+      corresponding to a wl_seat.
+
+      Depending on the version bound, the objects created from the bound
+      wl_data_device_manager object will have different requirements for
+      functioning properly. See wl_data_source.set_actions,
+      wl_data_offer.accept and wl_data_offer.finish for details.
+    </description>
+
+    <request name="create_data_source">
+      <description summary="create a new data source">
+	Create a new data source.
+      </description>
+      <arg name="id" type="new_id" interface="wl_data_source" summary="data source to create"/>
+    </request>
+
+    <request name="get_data_device">
+      <description summary="create a new data device">
+	Create a new data device for a given seat.
+      </description>
+      <arg name="id" type="new_id" interface="wl_data_device" summary="data device to create"/>
+      <arg name="seat" type="object" interface="wl_seat" summary="seat associated with the data device"/>
+    </request>
+
+    <!-- Version 3 additions -->
+
+    <enum name="dnd_action" bitfield="true" since="3">
+      <description summary="drag and drop actions">
+	This is a bitmask of the available/preferred actions in a
+	drag-and-drop operation.
+
+	In the compositor, the selected action is a result of matching the
+	actions offered by the source and destination sides.  "action" events
+	with a "none" action will be sent to both source and destination if
+	there is no match. All further checks will effectively happen on
+	(source actions ∩ destination actions).
+
+	In addition, compositors may also pick different actions in
+	reaction to key modifiers being pressed. One common design that
+	is used in major toolkits (and the behavior recommended for
+	compositors) is:
+
+	- If no modifiers are pressed, the first match (in bit order)
+	  will be used.
+	- Pressing Shift selects "move", if enabled in the mask.
+	- Pressing Control selects "copy", if enabled in the mask.
+
+	Behavior beyond that is considered implementation-dependent.
+	Compositors may for example bind other modifiers (like Alt/Meta)
+	or drags initiated with other buttons than BTN_LEFT to specific
+	actions (e.g. "ask").
+      </description>
+      <entry name="none" value="0" summary="no action"/>
+      <entry name="copy" value="1" summary="copy action"/>
+      <entry name="move" value="2" summary="move action"/>
+      <entry name="ask" value="4" summary="ask action"/>
+    </enum>
+  </interface>
+
+  <interface name="wl_shell" version="1">
+    <description summary="create desktop-style surfaces">
+      This interface is implemented by servers that provide
+      desktop-style user interfaces.
+
+      It allows clients to associate a wl_shell_surface with
+      a basic surface.
+    </description>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="given wl_surface has another role"/>
+    </enum>
+
+    <request name="get_shell_surface">
+      <description summary="create a shell surface from a surface">
+	Create a shell surface for an existing surface. This gives
+	the wl_surface the role of a shell surface. If the wl_surface
+	already has another role, it raises a protocol error.
+
+	Only one shell surface can be associated with a given surface.
+      </description>
+      <arg name="id" type="new_id" interface="wl_shell_surface" summary="shell surface to create"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="surface to be given the shell surface role"/>
+    </request>
+  </interface>
+
+  <interface name="wl_shell_surface" version="1">
+    <description summary="desktop-style metadata interface">
+      An interface that may be implemented by a wl_surface, for
+      implementations that provide a desktop-style user interface.
+
+      It provides requests to treat surfaces like toplevel, fullscreen
+      or popup windows, move, resize or maximize them, associate
+      metadata like title and class, etc.
+
+      On the server side the object is automatically destroyed when
+      the related wl_surface is destroyed. On the client side,
+      wl_shell_surface_destroy() must be called before destroying
+      the wl_surface object.
+    </description>
+
+    <request name="pong">
+      <description summary="respond to a ping event">
+	A client must respond to a ping event with a pong request or
+	the client may be deemed unresponsive.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the ping event"/>
+    </request>
+
+    <request name="move">
+      <description summary="start an interactive move">
+	Start a pointer-driven move of the surface.
+
+	This request must be used in response to a button press event.
+	The server may ignore move requests depending on the state of
+	the surface (e.g. fullscreen or maximized).
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="seat whose pointer is used"/>
+      <arg name="serial" type="uint" summary="serial number of the implicit grab on the pointer"/>
+    </request>
+
+    <enum name="resize" bitfield="true">
+      <description summary="edge values for resizing">
+	These values are used to indicate which edge of a surface
+	is being dragged in a resize operation. The server may
+	use this information to adapt its behavior, e.g. choose
+	an appropriate cursor image.
+      </description>
+      <entry name="none" value="0" summary="no edge"/>
+      <entry name="top" value="1" summary="top edge"/>
+      <entry name="bottom" value="2" summary="bottom edge"/>
+      <entry name="left" value="4" summary="left edge"/>
+      <entry name="top_left" value="5" summary="top and left edges"/>
+      <entry name="bottom_left" value="6" summary="bottom and left edges"/>
+      <entry name="right" value="8" summary="right edge"/>
+      <entry name="top_right" value="9" summary="top and right edges"/>
+      <entry name="bottom_right" value="10" summary="bottom and right edges"/>
+    </enum>
+
+    <request name="resize">
+      <description summary="start an interactive resize">
+	Start a pointer-driven resizing of the surface.
+
+	This request must be used in response to a button press event.
+	The server may ignore resize requests depending on the state of
+	the surface (e.g. fullscreen or maximized).
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="seat whose pointer is used"/>
+      <arg name="serial" type="uint" summary="serial number of the implicit grab on the pointer"/>
+      <arg name="edges" type="uint" enum="resize" summary="which edge or corner is being dragged"/>
+    </request>
+
+    <request name="set_toplevel">
+      <description summary="make the surface a toplevel surface">
+	Map the surface as a toplevel surface.
+
+	A toplevel surface is not fullscreen, maximized or transient.
+      </description>
+    </request>
+
+    <enum name="transient" bitfield="true">
+      <description summary="details of transient behaviour">
+	These flags specify details of the expected behaviour
+	of transient surfaces. Used in the set_transient request.
+      </description>
+      <entry name="inactive" value="0x1" summary="do not set keyboard focus"/>
+    </enum>
+
+    <request name="set_transient">
+      <description summary="make the surface a transient surface">
+	Map the surface relative to an existing surface.
+
+	The x and y arguments specify the location of the upper left
+	corner of the surface relative to the upper left corner of the
+	parent surface, in surface-local coordinates.
+
+	The flags argument controls details of the transient behaviour.
+      </description>
+      <arg name="parent" type="object" interface="wl_surface" summary="parent surface"/>
+      <arg name="x" type="int" summary="surface-local x coordinate"/>
+      <arg name="y" type="int" summary="surface-local y coordinate"/>
+      <arg name="flags" type="uint" enum="transient" summary="transient surface behavior"/>
+    </request>
+
+    <enum name="fullscreen_method">
+      <description summary="different method to set the surface fullscreen">
+	Hints to indicate to the compositor how to deal with a conflict
+	between the dimensions of the surface and the dimensions of the
+	output. The compositor is free to ignore this parameter.
+      </description>
+      <entry name="default" value="0" summary="no preference, apply default policy"/>
+      <entry name="scale" value="1" summary="scale, preserve the surface's aspect ratio and center on output"/>
+      <entry name="driver" value="2" summary="switch output mode to the smallest mode that can fit the surface, add black borders to compensate size mismatch"/>
+      <entry name="fill" value="3" summary="no upscaling, center on output and add black borders to compensate size mismatch"/>
+    </enum>
+
+    <request name="set_fullscreen">
+      <description summary="make the surface a fullscreen surface">
+	Map the surface as a fullscreen surface.
+
+	If an output parameter is given then the surface will be made
+	fullscreen on that output. If the client does not specify the
+	output then the compositor will apply its policy - usually
+	choosing the output on which the surface has the biggest surface
+	area.
+
+	The client may specify a method to resolve a size conflict
+	between the output size and the surface size - this is provided
+	through the method parameter.
+
+	The framerate parameter is used only when the method is set
+	to "driver", to indicate the preferred framerate. A value of 0
+	indicates that the client does not care about framerate.  The
+	framerate is specified in mHz, that is framerate of 60000 is 60Hz.
+
+	A method of "scale" or "driver" implies a scaling operation of
+	the surface, either via a direct scaling operation or a change of
+	the output mode. This will override any kind of output scaling, so
+	that mapping a surface with a buffer size equal to the mode can
+	fill the screen independent of buffer_scale.
+
+	A method of "fill" means we don't scale up the buffer, however
+	any output scale is applied. This means that you may run into
+	an edge case where the application maps a buffer with the same
+	size of the output mode but buffer_scale 1 (thus making a
+	surface larger than the output). In this case it is allowed to
+	downscale the results to fit the screen.
+
+	The compositor must reply to this request with a configure event
+	with the dimensions for the output on which the surface will
+	be made fullscreen.
+      </description>
+      <arg name="method" type="uint" enum="fullscreen_method" summary="method for resolving size conflict"/>
+      <arg name="framerate" type="uint" summary="framerate in mHz"/>
+      <arg name="output" type="object" interface="wl_output" allow-null="true"
+	   summary="output on which the surface is to be fullscreen"/>
+    </request>
+
+    <request name="set_popup">
+      <description summary="make the surface a popup surface">
+	Map the surface as a popup.
+
+	A popup surface is a transient surface with an added pointer
+	grab.
+
+	An existing implicit grab will be changed to owner-events mode,
+	and the popup grab will continue after the implicit grab ends
+	(i.e. releasing the mouse button does not cause the popup to
+	be unmapped).
+
+	The popup grab continues until the window is destroyed or a
+	mouse button is pressed in any other client's window. A click
+	in any of the client's surfaces is reported as normal, however,
+	clicks in other clients' surfaces will be discarded and trigger
+	the callback.
+
+	The x and y arguments specify the location of the upper left
+	corner of the surface relative to the upper left corner of the
+	parent surface, in surface-local coordinates.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="seat whose pointer is used"/>
+      <arg name="serial" type="uint" summary="serial number of the implicit grab on the pointer"/>
+      <arg name="parent" type="object" interface="wl_surface" summary="parent surface"/>
+      <arg name="x" type="int" summary="surface-local x coordinate"/>
+      <arg name="y" type="int" summary="surface-local y coordinate"/>
+      <arg name="flags" type="uint" enum="transient" summary="transient surface behavior"/>
+    </request>
+
+    <request name="set_maximized">
+      <description summary="make the surface a maximized surface">
+	Map the surface as a maximized surface.
+
+	If an output parameter is given then the surface will be
+	maximized on that output. If the client does not specify the
+	output then the compositor will apply its policy - usually
+	choosing the output on which the surface has the biggest surface
+	area.
+
+	The compositor will reply with a configure event telling
+	the expected new surface size. The operation is completed
+	on the next buffer attach to this surface.
+
+	A maximized surface typically fills the entire output it is
+	bound to, except for desktop elements such as panels. This is
+	the main difference between a maximized shell surface and a
+	fullscreen shell surface.
+
+	The details depend on the compositor implementation.
+      </description>
+      <arg name="output" type="object" interface="wl_output" allow-null="true"
+	   summary="output on which the surface is to be maximized"/>
+    </request>
+
+    <request name="set_title">
+      <description summary="set surface title">
+	Set a short title for the surface.
+
+	This string may be used to identify the surface in a task bar,
+	window list, or other user interface elements provided by the
+	compositor.
+
+	The string must be encoded in UTF-8.
+      </description>
+      <arg name="title" type="string" summary="surface title"/>
+    </request>
+
+    <request name="set_class">
+      <description summary="set surface class">
+	Set a class for the surface.
+
+	The surface class identifies the general class of applications
+	to which the surface belongs. A common convention is to use the
+	file name (or the full path if it is a non-standard location) of
+	the application's .desktop file as the class.
+      </description>
+      <arg name="class_" type="string" summary="surface class"/>
+    </request>
+
+    <event name="ping">
+      <description summary="ping client">
+	Ping a client to check if it is receiving events and sending
+	requests. A client is expected to reply with a pong request.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the ping"/>
+    </event>
+
+    <event name="configure">
+      <description summary="suggest resize">
+	The configure event asks the client to resize its surface.
+
+	The size is a hint, in the sense that the client is free to
+	ignore it if it doesn't resize, pick a smaller size (to
+	satisfy aspect ratio or resize in steps of NxM pixels).
+
+	The edges parameter provides a hint about how the surface
+	was resized. The client may use this information to decide
+	how to adjust its content to the new size (e.g. a scrolling
+	area might adjust its content position to leave the viewable
+	content unmoved).
+
+	The client is free to dismiss all but the last configure
+	event it received.
+
+	The width and height arguments specify the size of the window
+	in surface-local coordinates.
+      </description>
+      <arg name="edges" type="uint" enum="resize" summary="how the surface was resized"/>
+      <arg name="width" type="int" summary="new width of the surface"/>
+      <arg name="height" type="int" summary="new height of the surface"/>
+    </event>
+
+    <event name="popup_done">
+      <description summary="popup interaction is done">
+	The popup_done event is sent out when a popup grab is broken,
+	that is, when the user clicks a surface that doesn't belong
+	to the client owning the popup surface.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="wl_surface" version="4">
+    <description summary="an onscreen surface">
+      A surface is a rectangular area that is displayed on the screen.
+      It has a location, size and pixel contents.
+
+      The size of a surface (and relative positions on it) is described
+      in surface-local coordinates, which may differ from the buffer
+      coordinates of the pixel content, in case a buffer_transform
+      or a buffer_scale is used.
+
+      A surface without a "role" is fairly useless: a compositor does
+      not know where, when or how to present it. The role is the
+      purpose of a wl_surface. Examples of roles are a cursor for a
+      pointer (as set by wl_pointer.set_cursor), a drag icon
+      (wl_data_device.start_drag), a sub-surface
+      (wl_subcompositor.get_subsurface), and a window as defined by a
+      shell protocol (e.g. wl_shell.get_shell_surface).
+
+      A surface can have only one role at a time. Initially a
+      wl_surface does not have a role. Once a wl_surface is given a
+      role, it is set permanently for the whole lifetime of the
+      wl_surface object. Giving the current role again is allowed,
+      unless explicitly forbidden by the relevant interface
+      specification.
+
+      Surface roles are given by requests in other interfaces such as
+      wl_pointer.set_cursor. The request should explicitly mention
+      that this request gives a role to a wl_surface. Often, this
+      request also creates a new protocol object that represents the
+      role and adds additional functionality to wl_surface. When a
+      client wants to destroy a wl_surface, they must destroy this 'role
+      object' before the wl_surface.
+
+      Destroying the role object does not remove the role from the
+      wl_surface, but it may stop the wl_surface from "playing the role".
+      For instance, if a wl_subsurface object is destroyed, the wl_surface
+      it was created for will be unmapped and forget its position and
+      z-order. It is allowed to create a wl_subsurface for the same
+      wl_surface again, but it is not allowed to use the wl_surface as
+      a cursor (cursor is a different role than sub-surface, and role
+      switching is not allowed).
+    </description>
+
+    <enum name="error">
+      <description summary="wl_surface error values">
+	These errors can be emitted in response to wl_surface requests.
+      </description>
+      <entry name="invalid_scale" value="0" summary="buffer scale value is invalid"/>
+      <entry name="invalid_transform" value="1" summary="buffer transform value is invalid"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="delete surface">
+	Deletes the surface and invalidates its object ID.
+      </description>
+    </request>
+
+    <request name="attach">
+      <description summary="set the surface contents">
+	Set a buffer as the content of this surface.
+
+	The new size of the surface is calculated based on the buffer
+	size transformed by the inverse buffer_transform and the
+	inverse buffer_scale. This means that the supplied buffer
+	must be an integer multiple of the buffer_scale.
+
+	The x and y arguments specify the location of the new pending
+	buffer's upper left corner, relative to the current buffer's upper
+	left corner, in surface-local coordinates. In other words, the
+	x and y, combined with the new surface size define in which
+	directions the surface's size changes.
+
+	Surface contents are double-buffered state, see wl_surface.commit.
+
+	The initial surface contents are void; there is no content.
+	wl_surface.attach assigns the given wl_buffer as the pending
+	wl_buffer. wl_surface.commit makes the pending wl_buffer the new
+	surface contents, and the size of the surface becomes the size
+	calculated from the wl_buffer, as described above. After commit,
+	there is no pending buffer until the next attach.
+
+	Committing a pending wl_buffer allows the compositor to read the
+	pixels in the wl_buffer. The compositor may access the pixels at
+	any time after the wl_surface.commit request. When the compositor
+	will not access the pixels anymore, it will send the
+	wl_buffer.release event. Only after receiving wl_buffer.release,
+	the client may reuse the wl_buffer. A wl_buffer that has been
+	attached and then replaced by another attach instead of committed
+	will not receive a release event, and is not used by the
+	compositor.
+
+	Destroying the wl_buffer after wl_buffer.release does not change
+	the surface contents. However, if the client destroys the
+	wl_buffer before receiving the wl_buffer.release event, the surface
+	contents become undefined immediately.
+
+	If wl_surface.attach is sent with a NULL wl_buffer, the
+	following wl_surface.commit will remove the surface content.
+      </description>
+      <arg name="buffer" type="object" interface="wl_buffer" allow-null="true"
+	   summary="buffer of surface contents"/>
+      <arg name="x" type="int" summary="surface-local x coordinate"/>
+      <arg name="y" type="int" summary="surface-local y coordinate"/>
+    </request>
+
+    <request name="damage">
+      <description summary="mark part of the surface damaged">
+	This request is used to describe the regions where the pending
+	buffer is different from the current surface contents, and where
+	the surface therefore needs to be repainted. The compositor
+	ignores the parts of the damage that fall outside of the surface.
+
+	Damage is double-buffered state, see wl_surface.commit.
+
+	The damage rectangle is specified in surface-local coordinates,
+	where x and y specify the upper left corner of the damage rectangle.
+
+	The initial value for pending damage is empty: no damage.
+	wl_surface.damage adds pending damage: the new pending damage
+	is the union of old pending damage and the given rectangle.
+
+	wl_surface.commit assigns pending damage as the current damage,
+	and clears pending damage. The server will clear the current
+	damage as it repaints the surface.
+
+	Alternatively, damage can be posted with wl_surface.damage_buffer
+	which uses buffer coordinates instead of surface coordinates,
+	and is probably the preferred and intuitive way of doing this.
+      </description>
+      <arg name="x" type="int" summary="surface-local x coordinate"/>
+      <arg name="y" type="int" summary="surface-local y coordinate"/>
+      <arg name="width" type="int" summary="width of damage rectangle"/>
+      <arg name="height" type="int" summary="height of damage rectangle"/>
+    </request>
+
+    <request name="frame">
+      <description summary="request a frame throttling hint">
+	Request a notification when it is a good time to start drawing a new
+	frame, by creating a frame callback. This is useful for throttling
+	redrawing operations, and driving animations.
+
+	When a client is animating on a wl_surface, it can use the 'frame'
+	request to get notified when it is a good time to draw and commit the
+	next frame of animation. If the client commits an update earlier than
+	that, it is likely that some updates will not make it to the display,
+	and the client is wasting resources by drawing too often.
+
+	The frame request will take effect on the next wl_surface.commit.
+	The notification will only be posted for one frame unless
+	requested again. For a wl_surface, the notifications are posted in
+	the order the frame requests were committed.
+
+	The server must send the notifications so that a client
+	will not send excessive updates, while still allowing
+	the highest possible update rate for clients that wait for the reply
+	before drawing again. The server should give some time for the client
+	to draw and commit after sending the frame callback events to let it
+	hit the next output refresh.
+
+	A server should avoid signaling the frame callbacks if the
+	surface is not visible in any way, e.g. the surface is off-screen,
+	or completely obscured by other opaque surfaces.
+
+	The object returned by this request will be destroyed by the
+	compositor after the callback is fired and as such the client must not
+	attempt to use it after that point.
+
+	The callback_data passed in the callback is the current time, in
+	milliseconds, with an undefined base.
+      </description>
+      <arg name="callback" type="new_id" interface="wl_callback" summary="callback object for the frame request"/>
+    </request>
+
+    <request name="set_opaque_region">
+      <description summary="set opaque region">
+	This request sets the region of the surface that contains
+	opaque content.
+
+	The opaque region is an optimization hint for the compositor
+	that lets it optimize the redrawing of content behind opaque
+	regions.  Setting an opaque region is not required for correct
+	behaviour, but marking transparent content as opaque will result
+	in repaint artifacts.
+
+	The opaque region is specified in surface-local coordinates.
+
+	The compositor ignores the parts of the opaque region that fall
+	outside of the surface.
+
+	Opaque region is double-buffered state, see wl_surface.commit.
+
+	wl_surface.set_opaque_region changes the pending opaque region.
+	wl_surface.commit copies the pending region to the current region.
+	Otherwise, the pending and current regions are never changed.
+
+	The initial value for an opaque region is empty. Setting the pending
+	opaque region has copy semantics, and the wl_region object can be
+	destroyed immediately. A NULL wl_region causes the pending opaque
+	region to be set to empty.
+      </description>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+	   summary="opaque region of the surface"/>
+    </request>
+
+    <request name="set_input_region">
+      <description summary="set input region">
+	This request sets the region of the surface that can receive
+	pointer and touch events.
+
+	Input events happening outside of this region will try the next
+	surface in the server surface stack. The compositor ignores the
+	parts of the input region that fall outside of the surface.
+
+	The input region is specified in surface-local coordinates.
+
+	Input region is double-buffered state, see wl_surface.commit.
+
+	wl_surface.set_input_region changes the pending input region.
+	wl_surface.commit copies the pending region to the current region.
+	Otherwise the pending and current regions are never changed,
+	except cursor and icon surfaces are special cases, see
+	wl_pointer.set_cursor and wl_data_device.start_drag.
+
+	The initial value for an input region is infinite. That means the
+	whole surface will accept input. Setting the pending input region
+	has copy semantics, and the wl_region object can be destroyed
+	immediately. A NULL wl_region causes the input region to be set
+	to infinite.
+      </description>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+	   summary="input region of the surface"/>
+    </request>
+
+    <request name="commit">
+      <description summary="commit pending surface state">
+	Surface state (input, opaque, and damage regions, attached buffers,
+	etc.) is double-buffered. Protocol requests modify the pending state,
+	as opposed to the current state in use by the compositor. A commit
+	request atomically applies all pending state, replacing the current
+	state. After commit, the new pending state is as documented for each
+	related request.
+
+	On commit, a pending wl_buffer is applied first, and all other state
+	second. This means that all coordinates in double-buffered state are
+	relative to the new wl_buffer coming into use, except for
+	wl_surface.attach itself. If there is no pending wl_buffer, the
+	coordinates are relative to the current surface contents.
+
+	All requests that need a commit to become effective are documented
+	to affect double-buffered state.
+
+	Other interfaces may add further double-buffered surface state.
+      </description>
+    </request>
+
+    <event name="enter">
+      <description summary="surface enters an output">
+	This is emitted whenever a surface's creation, movement, or resizing
+	results in some part of it being within the scanout region of an
+	output.
+
+	Note that a surface may be overlapping with zero or more outputs.
+      </description>
+      <arg name="output" type="object" interface="wl_output" summary="output entered by the surface"/>
+    </event>
+
+    <event name="leave">
+      <description summary="surface leaves an output">
+	This is emitted whenever a surface's creation, movement, or resizing
+	results in it no longer having any part of it within the scanout region
+	of an output.
+      </description>
+      <arg name="output" type="object" interface="wl_output" summary="output left by the surface"/>
+    </event>
+
+    <!-- Version 2 additions -->
+
+    <request name="set_buffer_transform" since="2">
+      <description summary="sets the buffer transformation">
+	This request sets an optional transformation on how the compositor
+	interprets the contents of the buffer attached to the surface. The
+	accepted values for the transform parameter are the values for
+	wl_output.transform.
+
+	Buffer transform is double-buffered state, see wl_surface.commit.
+
+	A newly created surface has its buffer transformation set to normal.
+
+	wl_surface.set_buffer_transform changes the pending buffer
+	transformation. wl_surface.commit copies the pending buffer
+	transformation to the current one. Otherwise, the pending and current
+	values are never changed.
+
+	The purpose of this request is to allow clients to render content
+	according to the output transform, thus permitting the compositor to
+	use certain optimizations even if the display is rotated. Using
+	hardware overlays and scanning out a client buffer for fullscreen
+	surfaces are examples of such optimizations. Those optimizations are
+	highly dependent on the compositor implementation, so the use of this
+	request should be considered on a case-by-case basis.
+
+	Note that if the transform value includes 90 or 270 degree rotation,
+	the width of the buffer will become the surface height and the height
+	of the buffer will become the surface width.
+
+	If transform is not one of the values from the
+	wl_output.transform enum the invalid_transform protocol error
+	is raised.
+      </description>
+      <arg name="transform" type="int" enum="wl_output.transform"
+	   summary="transform for interpreting buffer contents"/>
+    </request>
+
+    <!-- Version 3 additions -->
+
+    <request name="set_buffer_scale" since="3">
+      <description summary="sets the buffer scaling factor">
+	This request sets an optional scaling factor on how the compositor
+	interprets the contents of the buffer attached to the window.
+
+	Buffer scale is double-buffered state, see wl_surface.commit.
+
+	A newly created surface has its buffer scale set to 1.
+
+	wl_surface.set_buffer_scale changes the pending buffer scale.
+	wl_surface.commit copies the pending buffer scale to the current one.
+	Otherwise, the pending and current values are never changed.
+
+	The purpose of this request is to allow clients to supply higher
+	resolution buffer data for use on high resolution outputs. It is
+	intended that you pick the same buffer scale as the scale of the
+	output that the surface is displayed on. This means the compositor
+	can avoid scaling when rendering the surface on that output.
+
+	Note that if the scale is larger than 1, then you have to attach
+	a buffer that is larger (by a factor of scale in each dimension)
+	than the desired surface size.
+
+	If scale is not positive the invalid_scale protocol error is
+	raised.
+      </description>
+      <arg name="scale" type="int"
+	   summary="positive scale for interpreting buffer contents"/>
+    </request>
+
+    <!-- Version 4 additions -->
+    <request name="damage_buffer" since="4">
+      <description summary="mark part of the surface damaged using buffer coordinates">
+	This request is used to describe the regions where the pending
+	buffer is different from the current surface contents, and where
+	the surface therefore needs to be repainted. The compositor
+	ignores the parts of the damage that fall outside of the surface.
+
+	Damage is double-buffered state, see wl_surface.commit.
+
+	The damage rectangle is specified in buffer coordinates,
+	where x and y specify the upper left corner of the damage rectangle.
+
+	The initial value for pending damage is empty: no damage.
+	wl_surface.damage_buffer adds pending damage: the new pending
+	damage is the union of old pending damage and the given rectangle.
+
+	wl_surface.commit assigns pending damage as the current damage,
+	and clears pending damage. The server will clear the current
+	damage as it repaints the surface.
+
+	This request differs from wl_surface.damage in only one way - it
+	takes damage in buffer coordinates instead of surface-local
+	coordinates. While this generally is more intuitive than surface
+	coordinates, it is especially desirable when using wp_viewport
+	or when a drawing library (like EGL) is unaware of buffer scale
+	and buffer transform.
+
+	Note: Because buffer transformation changes and damage requests may
+	be interleaved in the protocol stream, it is impossible to determine
+	the actual mapping between surface and buffer damage until
+	wl_surface.commit time. Therefore, compositors wishing to take both
+	kinds of damage into account will have to accumulate damage from the
+	two requests separately and only transform from one to the other
+	after receiving the wl_surface.commit.
+      </description>
+      <arg name="x" type="int" summary="buffer-local x coordinate"/>
+      <arg name="y" type="int" summary="buffer-local y coordinate"/>
+      <arg name="width" type="int" summary="width of damage rectangle"/>
+      <arg name="height" type="int" summary="height of damage rectangle"/>
+    </request>
+   </interface>
+
+  <interface name="wl_seat" version="6">
+    <description summary="group of input devices">
+      A seat is a group of keyboards, pointer and touch devices. This
+      object is published as a global during start up, or when such a
+      device is hot plugged.  A seat typically has a pointer and
+      maintains a keyboard focus and a pointer focus.
+    </description>
+
+    <enum name="capability" bitfield="true">
+      <description summary="seat capability bitmask">
+	This is a bitmask of capabilities this seat has; if a member is
+	set, then it is present on the seat.
+      </description>
+      <entry name="pointer" value="1" summary="the seat has pointer devices"/>
+      <entry name="keyboard" value="2" summary="the seat has one or more keyboards"/>
+      <entry name="touch" value="4" summary="the seat has touch devices"/>
+    </enum>
+
+    <event name="capabilities">
+      <description summary="seat capabilities changed">
+	This is emitted whenever a seat gains or loses the pointer,
+	keyboard or touch capabilities.  The argument is a capability
+	enum containing the complete set of capabilities this seat has.
+
+	When the pointer capability is added, a client may create a
+	wl_pointer object using the wl_seat.get_pointer request. This object
+	will receive pointer events until the capability is removed in the
+	future.
+
+	When the pointer capability is removed, a client should destroy the
+	wl_pointer objects associated with the seat where the capability was
+	removed, using the wl_pointer.release request. No further pointer
+	events will be received on these objects.
+
+	In some compositors, if a seat regains the pointer capability and a
+	client has a previously obtained wl_pointer object of version 4 or
+	less, that object may start sending pointer events again. This
+	behavior is considered a misinterpretation of the intended behavior
+	and must not be relied upon by the client. wl_pointer objects of
+	version 5 or later must not send events if created before the most
+	recent event notifying the client of an added pointer capability.
+
+	The above behavior also applies to wl_keyboard and wl_touch with the
+	keyboard and touch capabilities, respectively.
+      </description>
+      <arg name="capabilities" type="uint" enum="capability" summary="capabilities of the seat"/>
+    </event>
+
+    <request name="get_pointer">
+      <description summary="return pointer object">
+	The ID provided will be initialized to the wl_pointer interface
+	for this seat.
+
+	This request only takes effect if the seat has the pointer
+	capability, or has had the pointer capability in the past.
+	It is a protocol violation to issue this request on a seat that has
+	never had the pointer capability.
+      </description>
+      <arg name="id" type="new_id" interface="wl_pointer" summary="seat pointer"/>
+    </request>
+
+    <request name="get_keyboard">
+      <description summary="return keyboard object">
+	The ID provided will be initialized to the wl_keyboard interface
+	for this seat.
+
+	This request only takes effect if the seat has the keyboard
+	capability, or has had the keyboard capability in the past.
+	It is a protocol violation to issue this request on a seat that has
+	never had the keyboard capability.
+      </description>
+      <arg name="id" type="new_id" interface="wl_keyboard" summary="seat keyboard"/>
+    </request>
+
+    <request name="get_touch">
+      <description summary="return touch object">
+	The ID provided will be initialized to the wl_touch interface
+	for this seat.
+
+	This request only takes effect if the seat has the touch
+	capability, or has had the touch capability in the past.
+	It is a protocol violation to issue this request on a seat that has
+	never had the touch capability.
+      </description>
+      <arg name="id" type="new_id" interface="wl_touch" summary="seat touch interface"/>
+    </request>
+
+    <!-- Version 2 additions -->
+
+    <event name="name" since="2">
+      <description summary="unique identifier for this seat">
+	In a multiseat configuration this can be used by the client to help
+	identify which physical devices the seat represents. Based on
+	the seat configuration used by the compositor.
+      </description>
+      <arg name="name" type="string" summary="seat identifier"/>
+    </event>
+
+    <!-- Version 5 additions -->
+
+    <request name="release" type="destructor" since="5">
+      <description summary="release the seat object">
+	Using this request a client can tell the server that it is not going to
+	use the seat object anymore.
+      </description>
+    </request>
+
+  </interface>
+
+  <interface name="wl_pointer" version="6">
+    <description summary="pointer input device">
+      The wl_pointer interface represents one or more input devices,
+      such as mice, which control the pointer location and pointer_focus
+      of a seat.
+
+      The wl_pointer interface generates motion, enter and leave
+      events for the surfaces that the pointer is located over,
+      and button and axis events for button presses, button releases
+      and scrolling.
+    </description>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="given wl_surface has another role"/>
+    </enum>
+
+    <request name="set_cursor">
+      <description summary="set the pointer surface">
+	Set the pointer surface, i.e., the surface that contains the
+	pointer image (cursor). This request gives the surface the role
+	of a cursor. If the surface already has another role, it raises
+	a protocol error.
+
+	The cursor actually changes only if the pointer
+	focus for this device is one of the requesting client's surfaces
+	or the surface parameter is the current pointer surface. If
+	there was a previous surface set with this request it is
+	replaced. If surface is NULL, the pointer image is hidden.
+
+	The parameters hotspot_x and hotspot_y define the position of
+	the pointer surface relative to the pointer location. Its
+	top-left corner is always at (x, y) - (hotspot_x, hotspot_y),
+	where (x, y) are the coordinates of the pointer location, in
+	surface-local coordinates.
+
+	On surface.attach requests to the pointer surface, hotspot_x
+	and hotspot_y are decremented by the x and y parameters
+	passed to the request. Attach must be confirmed by
+	wl_surface.commit as usual.
+
+	The hotspot can also be updated by passing the currently set
+	pointer surface to this request with new values for hotspot_x
+	and hotspot_y.
+
+	The current and pending input regions of the wl_surface are
+	cleared, and wl_surface.set_input_region is ignored until the
+	wl_surface is no longer used as the cursor. When the use as a
+	cursor ends, the current and pending input regions become
+	undefined, and the wl_surface is unmapped.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the enter event"/>
+      <arg name="surface" type="object" interface="wl_surface" allow-null="true"
+	   summary="pointer surface"/>
+      <arg name="hotspot_x" type="int" summary="surface-local x coordinate"/>
+      <arg name="hotspot_y" type="int" summary="surface-local y coordinate"/>
+    </request>
+
+    <event name="enter">
+      <description summary="enter event">
+	Notification that this seat's pointer is focused on a certain
+	surface.
+
+	When a seat's focus enters a surface, the pointer image
+	is undefined and a client should respond to this event by setting
+	an appropriate pointer image with the set_cursor request.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the enter event"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="surface entered by the pointer"/>
+      <arg name="surface_x" type="fixed" summary="surface-local x coordinate"/>
+      <arg name="surface_y" type="fixed" summary="surface-local y coordinate"/>
+    </event>
+
+    <event name="leave">
+      <description summary="leave event">
+	Notification that this seat's pointer is no longer focused on
+	a certain surface.
+
+	The leave notification is sent before the enter notification
+	for the new focus.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the leave event"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="surface left by the pointer"/>
+    </event>
+
+    <event name="motion">
+      <description summary="pointer motion event">
+	Notification of pointer location change. The arguments
+	surface_x and surface_y are the location relative to the
+	focused surface.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="surface_x" type="fixed" summary="surface-local x coordinate"/>
+      <arg name="surface_y" type="fixed" summary="surface-local y coordinate"/>
+    </event>
+
+    <enum name="button_state">
+      <description summary="physical button state">
+	Describes the physical state of a button that produced the button
+	event.
+      </description>
+      <entry name="released" value="0" summary="the button is not pressed"/>
+      <entry name="pressed" value="1" summary="the button is pressed"/>
+    </enum>
+
+    <event name="button">
+      <description summary="pointer button event">
+	Mouse button click and release notifications.
+
+	The location of the click is given by the last motion or
+	enter event.
+	The time argument is a timestamp with millisecond
+	granularity, with an undefined base.
+
+	The button is a button code as defined in the Linux kernel's
+	linux/input-event-codes.h header file, e.g. BTN_LEFT.
+
+	Any 16-bit button code value is reserved for future additions to the
+	kernel's event code list. All other button codes above 0xFFFF are
+	currently undefined but may be used in future versions of this
+	protocol.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the button event"/>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="button" type="uint" summary="button that produced the event"/>
+      <arg name="state" type="uint" enum="button_state" summary="physical state of the button"/>
+    </event>
+
+    <enum name="axis">
+      <description summary="axis types">
+	Describes the axis types of scroll events.
+      </description>
+      <entry name="vertical_scroll" value="0" summary="vertical axis"/>
+      <entry name="horizontal_scroll" value="1" summary="horizontal axis"/>
+    </enum>
+
+    <event name="axis">
+      <description summary="axis event">
+	Scroll and other axis notifications.
+
+	For scroll events (vertical and horizontal scroll axes), the
+	value parameter is the length of a vector along the specified
+	axis in a coordinate space identical to those of motion events,
+	representing a relative movement along the specified axis.
+
+	For devices that support movements non-parallel to axes multiple
+	axis events will be emitted.
+
+	When applicable, for example for touch pads, the server can
+	choose to emit scroll events where the motion vector is
+	equivalent to a motion event vector.
+
+	When applicable, a client can transform its content relative to the
+	scroll distance.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="axis" type="uint" enum="axis" summary="axis type"/>
+      <arg name="value" type="fixed" summary="length of vector in surface-local coordinate space"/>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="release" type="destructor" since="3">
+      <description summary="release the pointer object">
+	Using this request a client can tell the server that it is not going to
+	use the pointer object anymore.
+
+	This request destroys the pointer proxy object, so clients must not call
+	wl_pointer_destroy() after using this request.
+      </description>
+    </request>
+
+    <!-- Version 5 additions -->
+
+    <event name="frame" since="5">
+      <description summary="end of a pointer event sequence">
+	Indicates the end of a set of events that logically belong together.
+	A client is expected to accumulate the data in all events within the
+	frame before proceeding.
+
+	All wl_pointer events before a wl_pointer.frame event belong
+	logically together. For example, in a diagonal scroll motion the
+	compositor will send an optional wl_pointer.axis_source event, two
+	wl_pointer.axis events (horizontal and vertical) and finally a
+	wl_pointer.frame event. The client may use this information to
+	calculate a diagonal vector for scrolling.
+
+	When multiple wl_pointer.axis events occur within the same frame,
+	the motion vector is the combined motion of all events.
+	When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
+	the same frame, this indicates that axis movement in one axis has
+	stopped but continues in the other axis.
+	When multiple wl_pointer.axis_stop events occur within the same
+	frame, this indicates that these axes stopped in the same instance.
+
+	A wl_pointer.frame event is sent for every logical event group,
+	even if the group only contains a single wl_pointer event.
+	Specifically, a client may get a sequence: motion, frame, button,
+	frame, axis, frame, axis_stop, frame.
+
+	The wl_pointer.enter and wl_pointer.leave events are logical events
+	generated by the compositor and not the hardware. These events are
+	also grouped by a wl_pointer.frame. When a pointer moves from one
+	surface to another, a compositor should group the
+	wl_pointer.leave event within the same wl_pointer.frame.
+	However, a client must not rely on wl_pointer.leave and
+	wl_pointer.enter being in the same wl_pointer.frame.
+	Compositor-specific policies may require the wl_pointer.leave and
+	wl_pointer.enter event being split across multiple wl_pointer.frame
+	groups.
+      </description>
+    </event>
+
+    <enum name="axis_source">
+      <description summary="axis source types">
+	Describes the source types for axis events. This indicates to the
+	client how an axis event was physically generated; a client may
+	adjust the user interface accordingly. For example, scroll events
+	from a "finger" source may be in a smooth coordinate space with
+	kinetic scrolling whereas a "wheel" source may be in discrete steps
+	of a number of lines.
+
+	The "continuous" axis source is a device generating events in a
+	continuous coordinate space, but using something other than a
+	finger. One example for this source is button-based scrolling where
+	the vertical motion of a device is converted to scroll events while
+	a button is held down.
+
+	The "wheel tilt" axis source indicates that the actual device is a
+	wheel but the scroll event is not caused by a rotation but a
+	(usually sideways) tilt of the wheel.
+      </description>
+      <entry name="wheel" value="0" summary="a physical wheel rotation" />
+      <entry name="finger" value="1" summary="finger on a touch surface" />
+      <entry name="continuous" value="2" summary="continuous coordinate space"/>
+      <entry name="wheel_tilt" value="3" summary="a physical wheel tilt" since="6"/>
+    </enum>
+
+    <event name="axis_source" since="5">
+      <description summary="axis source event">
+	Source information for scroll and other axes.
+
+	This event does not occur on its own. It is sent before a
+	wl_pointer.frame event and carries the source information for
+	all events within that frame.
+
+	The source specifies how this event was generated. If the source is
+	wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
+	sent when the user lifts the finger off the device.
+
+	If the source is wl_pointer.axis_source.wheel,
+	wl_pointer.axis_source.wheel_tilt or
+	wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
+	or may not be sent. Whether a compositor sends an axis_stop event
+	for these sources is hardware-specific and implementation-dependent;
+	clients must not rely on receiving an axis_stop event for these
+	scroll sources and should treat scroll sequences from these scroll
+	sources as unterminated by default.
+
+	This event is optional. If the source is unknown for a particular
+	axis event sequence, no event is sent.
+	Only one wl_pointer.axis_source event is permitted per frame.
+
+	The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+	not guaranteed.
+      </description>
+      <arg name="axis_source" type="uint" enum="axis_source" summary="source of the axis event"/>
+    </event>
+
+    <event name="axis_stop" since="5">
+      <description summary="axis stop event">
+	Stop notification for scroll and other axes.
+
+	For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
+	is sent to notify a client that the axis sequence has terminated.
+	This enables the client to implement kinetic scrolling.
+	See the wl_pointer.axis_source documentation for information on when
+	this event may be generated.
+
+	Any wl_pointer.axis events with the same axis_source after this
+	event should be considered as the start of a new axis motion.
+
+	The timestamp is to be interpreted identical to the timestamp in the
+	wl_pointer.axis event. The timestamp value may be the same as a
+	preceding wl_pointer.axis event.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="axis" type="uint" enum="axis" summary="the axis stopped with this event"/>
+    </event>
+
+    <event name="axis_discrete" since="5">
+      <description summary="axis click event">
+	Discrete step information for scroll and other axes.
+
+	This event carries the axis value of the wl_pointer.axis event in
+	discrete steps (e.g. mouse wheel clicks).
+
+	This event does not occur on its own, it is coupled with a
+	wl_pointer.axis event that represents this axis value on a
+	continuous scale. The protocol guarantees that each axis_discrete
+	event is always followed by exactly one axis event with the same
+	axis number within the same wl_pointer.frame. Note that the protocol
+	allows for other events to occur between the axis_discrete and
+	its coupled axis event, including other axis_discrete or axis
+	events.
+
+	This event is optional; continuous scrolling devices
+	like two-finger scrolling on touchpads do not have discrete
+	steps and do not generate this event.
+
+	The discrete value carries the directional information. e.g. a value
+	of -2 is two steps towards the negative direction of this axis.
+
+	The axis number is identical to the axis number in the associated
+	axis event.
+
+	The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
+	not guaranteed.
+      </description>
+      <arg name="axis" type="uint" enum="axis" summary="axis type"/>
+      <arg name="discrete" type="int" summary="number of steps"/>
+    </event>
+  </interface>
+
+  <interface name="wl_keyboard" version="6">
+    <description summary="keyboard input device">
+      The wl_keyboard interface represents one or more keyboards
+      associated with a seat.
+    </description>
+
+    <enum name="keymap_format">
+      <description summary="keyboard mapping format">
+	This specifies the format of the keymap provided to the
+	client with the wl_keyboard.keymap event.
+      </description>
+      <entry name="no_keymap" value="0"
+	     summary="no keymap; client must understand how to interpret the raw keycode"/>
+      <entry name="xkb_v1" value="1"
+	     summary="libxkbcommon compatible; to determine the xkb keycode, clients must add 8 to the key event keycode"/>
+    </enum>
+
+    <event name="keymap">
+      <description summary="keyboard mapping">
+	This event provides a file descriptor to the client which can be
+	memory-mapped to provide a keyboard mapping description.
+      </description>
+      <arg name="format" type="uint" enum="keymap_format" summary="keymap format"/>
+      <arg name="fd" type="fd" summary="keymap file descriptor"/>
+      <arg name="size" type="uint" summary="keymap size, in bytes"/>
+    </event>
+
+    <event name="enter">
+      <description summary="enter event">
+	Notification that this seat's keyboard focus is on a certain
+	surface.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the enter event"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="surface gaining keyboard focus"/>
+      <arg name="keys" type="array" summary="the currently pressed keys"/>
+    </event>
+
+    <event name="leave">
+      <description summary="leave event">
+	Notification that this seat's keyboard focus is no longer on
+	a certain surface.
+
+	The leave notification is sent before the enter notification
+	for the new focus.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the leave event"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="surface that lost keyboard focus"/>
+    </event>
+
+    <enum name="key_state">
+      <description summary="physical key state">
+	Describes the physical state of a key that produced the key event.
+      </description>
+      <entry name="released" value="0" summary="key is not pressed"/>
+      <entry name="pressed" value="1" summary="key is pressed"/>
+    </enum>
+
+    <event name="key">
+      <description summary="key event">
+	A key was pressed or released.
+	The time argument is a timestamp with millisecond
+	granularity, with an undefined base.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the key event"/>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="key" type="uint" summary="key that produced the event"/>
+      <arg name="state" type="uint" enum="key_state" summary="physical state of the key"/>
+    </event>
+
+    <event name="modifiers">
+      <description summary="modifier and group state">
+	Notifies clients that the modifier and/or group state has
+	changed, and it should update its local state.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the modifiers event"/>
+      <arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
+      <arg name="mods_latched" type="uint" summary="latched modifiers"/>
+      <arg name="mods_locked" type="uint" summary="locked modifiers"/>
+      <arg name="group" type="uint" summary="keyboard layout"/>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="release" type="destructor" since="3">
+      <description summary="release the keyboard object"/>
+    </request>
+
+    <!-- Version 4 additions -->
+
+    <event name="repeat_info" since="4">
+      <description summary="repeat rate and delay">
+	Informs the client about the keyboard's repeat rate and delay.
+
+	This event is sent as soon as the wl_keyboard object has been created,
+	and is guaranteed to be received by the client before any key press
+	event.
+
+	Negative values for either rate or delay are illegal. A rate of zero
+	will disable any repeating (regardless of the value of delay).
+
+	This event can be sent later on as well with a new value if necessary,
+	so clients should continue listening for the event past the creation
+	of wl_keyboard.
+      </description>
+      <arg name="rate" type="int"
+	   summary="the rate of repeating keys in characters per second"/>
+      <arg name="delay" type="int"
+	   summary="delay in milliseconds since key down until repeating starts"/>
+    </event>
+  </interface>
+
+  <interface name="wl_touch" version="6">
+    <description summary="touchscreen input device">
+      The wl_touch interface represents a touchscreen
+      associated with a seat.
+
+      Touch interactions can consist of one or more contacts.
+      For each contact, a series of events is generated, starting
+      with a down event, followed by zero or more motion events,
+      and ending with an up event. Events relating to the same
+      contact point can be identified by the ID of the sequence.
+    </description>
+
+    <event name="down">
+      <description summary="touch down event and beginning of a touch sequence">
+	A new touch point has appeared on the surface. This touch point is
+	assigned a unique ID. Future events from this touch point reference
+	this ID. The ID ceases to be valid after a touch up event and may be
+	reused in the future.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the touch down event"/>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="surface" type="object" interface="wl_surface" summary="surface touched"/>
+      <arg name="id" type="int" summary="the unique ID of this touch point"/>
+      <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+      <arg name="y" type="fixed" summary="surface-local y coordinate"/>
+    </event>
+
+    <event name="up">
+      <description summary="end of a touch event sequence">
+	The touch point has disappeared. No further events will be sent for
+	this touch point and the touch point's ID is released and may be
+	reused in a future touch down event.
+      </description>
+      <arg name="serial" type="uint" summary="serial number of the touch up event"/>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="id" type="int" summary="the unique ID of this touch point"/>
+    </event>
+
+    <event name="motion">
+      <description summary="update of touch point coordinates">
+	A touch point has changed coordinates.
+      </description>
+      <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+      <arg name="id" type="int" summary="the unique ID of this touch point"/>
+      <arg name="x" type="fixed" summary="surface-local x coordinate"/>
+      <arg name="y" type="fixed" summary="surface-local y coordinate"/>
+    </event>
+
+    <event name="frame">
+      <description summary="end of touch frame event">
+	Indicates the end of a set of events that logically belong together.
+	A client is expected to accumulate the data in all events within the
+	frame before proceeding.
+
+	A wl_touch.frame terminates at least one event but otherwise no
+	guarantee is provided about the set of events within a frame. A client
+	must assume that any state not updated in a frame is unchanged from the
+	previously known state.
+      </description>
+    </event>
+
+    <event name="cancel">
+      <description summary="touch session cancelled">
+	Sent if the compositor decides the touch stream is a global
+	gesture. No further events are sent to the clients from that
+	particular gesture. Touch cancellation applies to all touch points
+	currently active on this client's surface. The client is
+	responsible for finalizing the touch points, future touch points on
+	this surface may reuse the touch point ID.
+      </description>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="release" type="destructor" since="3">
+      <description summary="release the touch object"/>
+    </request>
+
+    <!-- Version 6 additions -->
+
+    <event name="shape" since="6">
+      <description summary="update shape of touch point">
+	Sent when a touchpoint has changed its shape.
+
+	This event does not occur on its own. It is sent before a
+	wl_touch.frame event and carries the new shape information for
+	any previously reported, or new touch points of that frame.
+
+	Other events describing the touch point such as wl_touch.down,
+	wl_touch.motion or wl_touch.orientation may be sent within the
+	same wl_touch.frame. A client should treat these events as a single
+	logical touch point update. The order of wl_touch.shape,
+	wl_touch.orientation and wl_touch.motion is not guaranteed.
+	A wl_touch.down event is guaranteed to occur before the first
+	wl_touch.shape event for this touch ID but both events may occur within
+	the same wl_touch.frame.
+
+	A touchpoint shape is approximated by an ellipse through the major and
+	minor axis length. The major axis length describes the longer diameter
+	of the ellipse, while the minor axis length describes the shorter
+	diameter. Major and minor are orthogonal and both are specified in
+	surface-local coordinates. The center of the ellipse is always at the
+	touchpoint location as reported by wl_touch.down or wl_touch.move.
+
+	This event is only sent by the compositor if the touch device supports
+	shape reports. The client has to make reasonable assumptions about the
+	shape if it did not receive this event.
+      </description>
+      <arg name="id" type="int" summary="the unique ID of this touch point"/>
+      <arg name="major" type="fixed" summary="length of the major axis in surface-local coordinates"/>
+      <arg name="minor" type="fixed" summary="length of the minor axis in surface-local coordinates"/>
+    </event>
+
+    <event name="orientation" since="6">
+      <description summary="update orientation of touch point">
+	Sent when a touchpoint has changed its orientation.
+
+	This event does not occur on its own. It is sent before a
+	wl_touch.frame event and carries the new shape information for
+	any previously reported, or new touch points of that frame.
+
+	Other events describing the touch point such as wl_touch.down,
+	wl_touch.motion or wl_touch.shape may be sent within the
+	same wl_touch.frame. A client should treat these events as a single
+	logical touch point update. The order of wl_touch.shape,
+	wl_touch.orientation and wl_touch.motion is not guaranteed.
+	A wl_touch.down event is guaranteed to occur before the first
+	wl_touch.orientation event for this touch ID but both events may occur
+	within the same wl_touch.frame.
+
+	The orientation describes the clockwise angle of a touchpoint's major
+	axis to the positive surface y-axis and is normalized to the -180 to
+	+180 degree range. The granularity of orientation depends on the touch
+	device, some devices only support binary rotation values between 0 and
+	90 degrees.
+
+	This event is only sent by the compositor if the touch device supports
+	orientation reports.
+      </description>
+      <arg name="id" type="int" summary="the unique ID of this touch point"/>
+      <arg name="orientation" type="fixed" summary="angle between major axis and positive surface y-axis in degrees"/>
+    </event>
+  </interface>
+
+  <interface name="wl_output" version="3">
+    <description summary="compositor output region">
+      An output describes part of the compositor geometry.  The
+      compositor works in the 'compositor coordinate system' and an
+      output corresponds to a rectangular area in that space that is
+      actually visible.  This typically corresponds to a monitor that
+      displays part of the compositor space.  This object is published
+      as global during start up, or when a monitor is hotplugged.
+    </description>
+
+    <enum name="subpixel">
+      <description summary="subpixel geometry information">
+	This enumeration describes how the physical
+	pixels on an output are laid out.
+      </description>
+      <entry name="unknown" value="0" summary="unknown geometry"/>
+      <entry name="none" value="1" summary="no geometry"/>
+      <entry name="horizontal_rgb" value="2" summary="horizontal RGB"/>
+      <entry name="horizontal_bgr" value="3" summary="horizontal BGR"/>
+      <entry name="vertical_rgb" value="4" summary="vertical RGB"/>
+      <entry name="vertical_bgr" value="5" summary="vertical BGR"/>
+    </enum>
+
+    <enum name="transform">
+      <description summary="transform from framebuffer to output">
+	This describes the transform that a compositor will apply to a
+	surface to compensate for the rotation or mirroring of an
+	output device.
+
+	The flipped values correspond to an initial flip around a
+	vertical axis followed by rotation.
+
+	The purpose is mainly to allow clients to render accordingly and
+	tell the compositor, so that for fullscreen surfaces, the
+	compositor will still be able to scan out directly from client
+	surfaces.
+      </description>
+      <entry name="normal" value="0" summary="no transform"/>
+      <entry name="90" value="1" summary="90 degrees counter-clockwise"/>
+      <entry name="180" value="2" summary="180 degrees counter-clockwise"/>
+      <entry name="270" value="3" summary="270 degrees counter-clockwise"/>
+      <entry name="flipped" value="4" summary="180 degree flip around a vertical axis"/>
+      <entry name="flipped_90" value="5" summary="flip and rotate 90 degrees counter-clockwise"/>
+      <entry name="flipped_180" value="6" summary="flip and rotate 180 degrees counter-clockwise"/>
+      <entry name="flipped_270" value="7" summary="flip and rotate 270 degrees counter-clockwise"/>
+    </enum>
+
+    <event name="geometry">
+      <description summary="properties of the output">
+	The geometry event describes geometric properties of the output.
+	The event is sent when binding to the output object and whenever
+	any of the properties change.
+      </description>
+      <arg name="x" type="int"
+	   summary="x position within the global compositor space"/>
+      <arg name="y" type="int"
+	   summary="y position within the global compositor space"/>
+      <arg name="physical_width" type="int"
+	   summary="width in millimeters of the output"/>
+      <arg name="physical_height" type="int"
+	   summary="height in millimeters of the output"/>
+      <arg name="subpixel" type="int" enum="subpixel"
+	   summary="subpixel orientation of the output"/>
+      <arg name="make" type="string"
+	   summary="textual description of the manufacturer"/>
+      <arg name="model" type="string"
+	   summary="textual description of the model"/>
+      <arg name="transform" type="int" enum="transform"
+	   summary="transform that maps framebuffer to output"/>
+    </event>
+
+    <enum name="mode" bitfield="true">
+      <description summary="mode information">
+	These flags describe properties of an output mode.
+	They are used in the flags bitfield of the mode event.
+      </description>
+      <entry name="current" value="0x1"
+	     summary="indicates this is the current mode"/>
+      <entry name="preferred" value="0x2"
+	     summary="indicates this is the preferred mode"/>
+    </enum>
+
+    <event name="mode">
+      <description summary="advertise available modes for the output">
+	The mode event describes an available mode for the output.
+
+	The event is sent when binding to the output object and there
+	will always be one mode, the current mode.  The event is sent
+	again if an output changes mode, for the mode that is now
+	current.  In other words, the current mode is always the last
+	mode that was received with the current flag set.
+
+	The size of a mode is given in physical hardware units of
+	the output device. This is not necessarily the same as
+	the output size in the global compositor space. For instance,
+	the output may be scaled, as described in wl_output.scale,
+	or transformed, as described in wl_output.transform.
+      </description>
+      <arg name="flags" type="uint" enum="mode" summary="bitfield of mode flags"/>
+      <arg name="width" type="int" summary="width of the mode in hardware units"/>
+      <arg name="height" type="int" summary="height of the mode in hardware units"/>
+      <arg name="refresh" type="int" summary="vertical refresh rate in mHz"/>
+    </event>
+
+    <!-- Version 2 additions -->
+
+    <event name="done" since="2">
+      <description summary="sent all information about output">
+	This event is sent after all other properties have been
+	sent after binding to the output object and after any
+	other property changes done after that. This allows
+	changes to the output properties to be seen as
+	atomic, even if they happen via multiple events.
+      </description>
+    </event>
+
+    <event name="scale" since="2">
+      <description summary="output scaling properties">
+	This event contains scaling geometry information
+	that is not in the geometry event. It may be sent after
+	binding the output object or if the output scale changes
+	later. If it is not sent, the client should assume a
+	scale of 1.
+
+	A scale larger than 1 means that the compositor will
+	automatically scale surface buffers by this amount
+	when rendering. This is used for very high resolution
+	displays where applications rendering at the native
+	resolution would be too small to be legible.
+
+	It is intended that scaling aware clients track the
+	current output of a surface, and if it is on a scaled
+	output it should use wl_surface.set_buffer_scale with
+	the scale of the output. That way the compositor can
+	avoid scaling the surface, and the client can supply
+	a higher detail image.
+      </description>
+      <arg name="factor" type="int" summary="scaling factor of output"/>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="release" type="destructor" since="3">
+      <description summary="release the output object">
+	Using this request a client can tell the server that it is not going to
+	use the output object anymore.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="wl_region" version="1">
+    <description summary="region interface">
+      A region object describes an area.
+
+      Region objects are used to describe the opaque and input
+      regions of a surface.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy region">
+	Destroy the region.  This will invalidate the object ID.
+      </description>
+    </request>
+
+    <request name="add">
+      <description summary="add rectangle to region">
+	Add the specified rectangle to the region.
+      </description>
+      <arg name="x" type="int" summary="region-local x coordinate"/>
+      <arg name="y" type="int" summary="region-local y coordinate"/>
+      <arg name="width" type="int" summary="rectangle width"/>
+      <arg name="height" type="int" summary="rectangle height"/>
+    </request>
+
+    <request name="subtract">
+      <description summary="subtract rectangle from region">
+	Subtract the specified rectangle from the region.
+      </description>
+      <arg name="x" type="int" summary="region-local x coordinate"/>
+      <arg name="y" type="int" summary="region-local y coordinate"/>
+      <arg name="width" type="int" summary="rectangle width"/>
+      <arg name="height" type="int" summary="rectangle height"/>
+    </request>
+  </interface>
+
+  <interface name="wl_subcompositor" version="1">
+    <description summary="sub-surface compositing">
+      The global interface exposing sub-surface compositing capabilities.
+      A wl_surface, that has sub-surfaces associated, is called the
+      parent surface. Sub-surfaces can be arbitrarily nested and create
+      a tree of sub-surfaces.
+
+      The root surface in a tree of sub-surfaces is the main
+      surface. The main surface cannot be a sub-surface, because
+      sub-surfaces must always have a parent.
+
+      A main surface with its sub-surfaces forms a (compound) window.
+      For window management purposes, this set of wl_surface objects is
+      to be considered as a single window, and it should also behave as
+      such.
+
+      The aim of sub-surfaces is to offload some of the compositing work
+      within a window from clients to the compositor. A prime example is
+      a video player with decorations and video in separate wl_surface
+      objects. This should allow the compositor to pass YUV video buffer
+      processing to dedicated overlay hardware when possible.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="unbind from the subcompositor interface">
+	Informs the server that the client will not be using this
+	protocol object anymore. This does not affect any other
+	objects, wl_subsurface objects included.
+      </description>
+    </request>
+
+    <enum name="error">
+      <entry name="bad_surface" value="0"
+	     summary="the to-be sub-surface is invalid"/>
+    </enum>
+
+    <request name="get_subsurface">
+      <description summary="give a surface the role sub-surface">
+	Create a sub-surface interface for the given surface, and
+	associate it with the given parent surface. This turns a
+	plain wl_surface into a sub-surface.
+
+	The to-be sub-surface must not already have another role, and it
+	must not have an existing wl_subsurface object. Otherwise a protocol
+	error is raised.
+      </description>
+      <arg name="id" type="new_id" interface="wl_subsurface"
+	   summary="the new sub-surface object ID"/>
+      <arg name="surface" type="object" interface="wl_surface"
+	   summary="the surface to be turned into a sub-surface"/>
+      <arg name="parent" type="object" interface="wl_surface"
+	   summary="the parent surface"/>
+    </request>
+  </interface>
+
+  <interface name="wl_subsurface" version="1">
+    <description summary="sub-surface interface to a wl_surface">
+      An additional interface to a wl_surface object, which has been
+      made a sub-surface. A sub-surface has one parent surface. A
+      sub-surface's size and position are not limited to that of the parent.
+      Particularly, a sub-surface is not automatically clipped to its
+      parent's area.
+
+      A sub-surface becomes mapped, when a non-NULL wl_buffer is applied
+      and the parent surface is mapped. The order of which one happens
+      first is irrelevant. A sub-surface is hidden if the parent becomes
+      hidden, or if a NULL wl_buffer is applied. These rules apply
+      recursively through the tree of surfaces.
+
+      The behaviour of a wl_surface.commit request on a sub-surface
+      depends on the sub-surface's mode. The possible modes are
+      synchronized and desynchronized, see methods
+      wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
+      mode caches the wl_surface state to be applied when the parent's
+      state gets applied, and desynchronized mode applies the pending
+      wl_surface state directly. A sub-surface is initially in the
+      synchronized mode.
+
+      Sub-surfaces have also other kind of state, which is managed by
+      wl_subsurface requests, as opposed to wl_surface requests. This
+      state includes the sub-surface position relative to the parent
+      surface (wl_subsurface.set_position), and the stacking order of
+      the parent and its sub-surfaces (wl_subsurface.place_above and
+      .place_below). This state is applied when the parent surface's
+      wl_surface state is applied, regardless of the sub-surface's mode.
+      As the exception, set_sync and set_desync are effective immediately.
+
+      The main surface can be thought to be always in desynchronized mode,
+      since it does not have a parent in the sub-surfaces sense.
+
+      Even if a sub-surface is in desynchronized mode, it will behave as
+      in synchronized mode, if its parent surface behaves as in
+      synchronized mode. This rule is applied recursively throughout the
+      tree of surfaces. This means, that one can set a sub-surface into
+      synchronized mode, and then assume that all its child and grand-child
+      sub-surfaces are synchronized, too, without explicitly setting them.
+
+      If the wl_surface associated with the wl_subsurface is destroyed, the
+      wl_subsurface object becomes inert. Note, that destroying either object
+      takes effect immediately. If you need to synchronize the removal
+      of a sub-surface to the parent surface update, unmap the sub-surface
+      first by attaching a NULL wl_buffer, update parent, and then destroy
+      the sub-surface.
+
+      If the parent wl_surface object is destroyed, the sub-surface is
+      unmapped.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="remove sub-surface interface">
+	The sub-surface interface is removed from the wl_surface object
+	that was turned into a sub-surface with a
+	wl_subcompositor.get_subsurface request. The wl_surface's association
+	to the parent is deleted, and the wl_surface loses its role as
+	a sub-surface. The wl_surface is unmapped.
+      </description>
+    </request>
+
+    <enum name="error">
+      <entry name="bad_surface" value="0"
+	     summary="wl_surface is not a sibling or the parent"/>
+    </enum>
+
+    <request name="set_position">
+      <description summary="reposition the sub-surface">
+	This schedules a sub-surface position change.
+	The sub-surface will be moved so that its origin (top left
+	corner pixel) will be at the location x, y of the parent surface
+	coordinate system. The coordinates are not restricted to the parent
+	surface area. Negative values are allowed.
+
+	The scheduled coordinates will take effect whenever the state of the
+	parent surface is applied. When this happens depends on whether the
+	parent surface is in synchronized mode or not. See
+	wl_subsurface.set_sync and wl_subsurface.set_desync for details.
+
+	If more than one set_position request is invoked by the client before
+	the commit of the parent surface, the position of a new request always
+	replaces the scheduled position from any previous request.
+
+	The initial position is 0, 0.
+      </description>
+      <arg name="x" type="int" summary="x coordinate in the parent surface"/>
+      <arg name="y" type="int" summary="y coordinate in the parent surface"/>
+    </request>
+
+    <request name="place_above">
+      <description summary="restack the sub-surface">
+	This sub-surface is taken from the stack, and put back just
+	above the reference surface, changing the z-order of the sub-surfaces.
+	The reference surface must be one of the sibling surfaces, or the
+	parent surface. Using any other surface, including this sub-surface,
+	will cause a protocol error.
+
+	The z-order is double-buffered. Requests are handled in order and
+	applied immediately to a pending state. The final pending state is
+	copied to the active state the next time the state of the parent
+	surface is applied. When this happens depends on whether the parent
+	surface is in synchronized mode or not. See wl_subsurface.set_sync and
+	wl_subsurface.set_desync for details.
+
+	A new sub-surface is initially added as the top-most in the stack
+	of its siblings and parent.
+      </description>
+      <arg name="sibling" type="object" interface="wl_surface"
+	   summary="the reference surface"/>
+    </request>
+
+    <request name="place_below">
+      <description summary="restack the sub-surface">
+	The sub-surface is placed just below the reference surface.
+	See wl_subsurface.place_above.
+      </description>
+      <arg name="sibling" type="object" interface="wl_surface"
+	   summary="the reference surface"/>
+    </request>
+
+    <request name="set_sync">
+      <description summary="set sub-surface to synchronized mode">
+	Change the commit behaviour of the sub-surface to synchronized
+	mode, also described as the parent dependent mode.
+
+	In synchronized mode, wl_surface.commit on a sub-surface will
+	accumulate the committed state in a cache, but the state will
+	not be applied and hence will not change the compositor output.
+	The cached state is applied to the sub-surface immediately after
+	the parent surface's state is applied. This ensures atomic
+	updates of the parent and all its synchronized sub-surfaces.
+	Applying the cached state will invalidate the cache, so further
+	parent surface commits do not (re-)apply old state.
+
+	See wl_subsurface for the recursive effect of this mode.
+      </description>
+    </request>
+
+    <request name="set_desync">
+      <description summary="set sub-surface to desynchronized mode">
+	Change the commit behaviour of the sub-surface to desynchronized
+	mode, also described as independent or freely running mode.
+
+	In desynchronized mode, wl_surface.commit on a sub-surface will
+	apply the pending state directly, without caching, as happens
+	normally with a wl_surface. Calling wl_surface.commit on the
+	parent surface has no effect on the sub-surface's wl_surface
+	state. This mode allows a sub-surface to be updated on its own.
+
+	If cached state exists when wl_surface.commit is called in
+	desynchronized mode, the pending state is added to the cached
+	state, and applied as a whole. This invalidates the cache.
+
+	Note: even if a sub-surface is set to desynchronized, a parent
+	sub-surface may override it to behave as synchronized. For details,
+	see wl_subsurface.
+
+	If a surface's parent surface behaves as desynchronized, then
+	the cached state is applied on set_desync.
+      </description>
+    </request>
+  </interface>
+
+</protocol>
diff --git a/source/wayland-protocols/xdg-shell-unstable-v6.xml b/source/wayland-protocols/xdg-shell-unstable-v6.xml
new file mode 100644
index 0000000..1c0f924
--- /dev/null
+++ b/source/wayland-protocols/xdg-shell-unstable-v6.xml
@@ -0,0 +1,1044 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_shell_unstable_v6">
+
+  <copyright>
+    Copyright © 2008-2013 Kristian Høgsberg
+    Copyright © 2013      Rafael Antognolli
+    Copyright © 2013      Jasper St. Pierre
+    Copyright © 2010-2013 Intel Corporation
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="zxdg_shell_v6" version="1">
+    <description summary="create desktop-style surfaces">
+      xdg_shell allows clients to turn a wl_surface into a "real window"
+      which can be dragged, resized, stacked, and moved around by the
+      user. Everything about this interface is suited towards traditional
+      desktop environments.
+    </description>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="given wl_surface has another role"/>
+      <entry name="defunct_surfaces" value="1"
+	     summary="xdg_shell was destroyed before children"/>
+      <entry name="not_the_topmost_popup" value="2"
+	     summary="the client tried to map or destroy a non-topmost popup"/>
+      <entry name="invalid_popup_parent" value="3"
+	     summary="the client specified an invalid popup parent surface"/>
+      <entry name="invalid_surface_state" value="4"
+	     summary="the client provided an invalid surface state"/>
+      <entry name="invalid_positioner" value="5"
+	     summary="the client provided an invalid positioner"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy xdg_shell">
+	Destroy this xdg_shell object.
+
+	Destroying a bound xdg_shell object while there are surfaces
+	still alive created by this xdg_shell object instance is illegal
+	and will result in a protocol error.
+      </description>
+    </request>
+
+    <request name="create_positioner">
+      <description summary="create a positioner object">
+	Create a positioner object. A positioner object is used to position
+	surfaces relative to some parent surface. See the interface description
+	and xdg_surface.get_popup for details.
+      </description>
+      <arg name="id" type="new_id" interface="zxdg_positioner_v6"/>
+    </request>
+
+    <request name="get_xdg_surface">
+      <description summary="create a shell surface from a surface">
+	This creates an xdg_surface for the given surface. While xdg_surface
+	itself is not a role, the corresponding surface may only be assigned
+	a role extending xdg_surface, such as xdg_toplevel or xdg_popup.
+
+	This creates an xdg_surface for the given surface. An xdg_surface is
+	used as basis to define a role to a given surface, such as xdg_toplevel
+	or xdg_popup. It also manages functionality shared between xdg_surface
+	based surface roles.
+
+	See the documentation of xdg_surface for more details about what an
+	xdg_surface is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="zxdg_surface_v6"/>
+      <arg name="surface" type="object" interface="wl_surface"/>
+    </request>
+
+    <request name="pong">
+      <description summary="respond to a ping event">
+	A client must respond to a ping event with a pong request or
+	the client may be deemed unresponsive. See xdg_shell.ping.
+      </description>
+      <arg name="serial" type="uint" summary="serial of the ping event"/>
+    </request>
+
+    <event name="ping">
+      <description summary="check if the client is alive">
+	The ping event asks the client if it's still alive. Pass the
+	serial specified in the event back to the compositor by sending
+	a "pong" request back with the specified serial. See xdg_shell.ping.
+
+	Compositors can use this to determine if the client is still
+	alive. It's unspecified what will happen if the client doesn't
+	respond to the ping request, or in what timeframe. Clients should
+	try to respond in a reasonable amount of time.
+
+	A compositor is free to ping in any way it wants, but a client must
+	always respond to any xdg_shell object it created.
+      </description>
+      <arg name="serial" type="uint" summary="pass this to the pong request"/>
+    </event>
+  </interface>
+
+  <interface name="zxdg_positioner_v6" version="1">
+    <description summary="child surface positioner">
+      The xdg_positioner provides a collection of rules for the placement of a
+      child surface relative to a parent surface. Rules can be defined to ensure
+      the child surface remains within the visible area's borders, and to
+      specify how the child surface changes its position, such as sliding along
+      an axis, or flipping around a rectangle. These positioner-created rules are
+      constrained by the requirement that a child surface must intersect with or
+      be at least partially adjacent to its parent surface.
+
+      See the various requests for details about possible rules.
+
+      At the time of the request, the compositor makes a copy of the rules
+      specified by the xdg_positioner. Thus, after the request is complete the
+      xdg_positioner object can be destroyed or reused; further changes to the
+      object will have no effect on previous usages.
+
+      For an xdg_positioner object to be considered complete, it must have a
+      non-zero size set by set_size, and a non-zero anchor rectangle set by
+      set_anchor_rect. Passing an incomplete xdg_positioner object when
+      positioning a surface raises an error.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_input" value="0" summary="invalid input provided"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_positioner object">
+	Notify the compositor that the xdg_positioner will no longer be used.
+      </description>
+    </request>
+
+    <request name="set_size">
+      <description summary="set the size of the to-be positioned rectangle">
+	Set the size of the surface that is to be positioned with the positioner
+	object. The size is in surface-local coordinates and corresponds to the
+	window geometry. See xdg_surface.set_window_geometry.
+
+	If a zero or negative size is set the invalid_input error is raised.
+      </description>
+      <arg name="width" type="int" summary="width of positioned rectangle"/>
+      <arg name="height" type="int" summary="height of positioned rectangle"/>
+    </request>
+
+    <request name="set_anchor_rect">
+      <description summary="set the anchor rectangle within the parent surface">
+	Specify the anchor rectangle within the parent surface that the child
+	surface will be placed relative to. The rectangle is relative to the
+	window geometry as defined by xdg_surface.set_window_geometry of the
+	parent surface. The rectangle must be at least 1x1 large.
+
+	When the xdg_positioner object is used to position a child surface, the
+	anchor rectangle may not extend outside the window geometry of the
+	positioned child's parent surface.
+
+	If a zero or negative size is set the invalid_input error is raised.
+      </description>
+      <arg name="x" type="int" summary="x position of anchor rectangle"/>
+      <arg name="y" type="int" summary="y position of anchor rectangle"/>
+      <arg name="width" type="int" summary="width of anchor rectangle"/>
+      <arg name="height" type="int" summary="height of anchor rectangle"/>
+    </request>
+
+    <enum name="anchor" bitfield="true">
+      <entry name="none" value="0"
+	     summary="the center of the anchor rectangle"/>
+      <entry name="top" value="1"
+	     summary="the top edge of the anchor rectangle"/>
+      <entry name="bottom" value="2"
+	     summary="the bottom edge of the anchor rectangle"/>
+      <entry name="left" value="4"
+	     summary="the left edge of the anchor rectangle"/>
+      <entry name="right" value="8"
+	     summary="the right edge of the anchor rectangle"/>
+    </enum>
+
+    <request name="set_anchor">
+      <description summary="set anchor rectangle anchor edges">
+	Defines a set of edges for the anchor rectangle. These are used to
+	derive an anchor point that the child surface will be positioned
+	relative to. If two orthogonal edges are specified (e.g. 'top' and
+	'left'), then the anchor point will be the intersection of the edges
+	(e.g. the top left position of the rectangle); otherwise, the derived
+	anchor point will be centered on the specified edge, or in the center of
+	the anchor rectangle if no edge is specified.
+
+	If two parallel anchor edges are specified (e.g. 'left' and 'right'),
+	the invalid_input error is raised.
+      </description>
+      <arg name="anchor" type="uint" enum="anchor"
+	   summary="bit mask of anchor edges"/>
+    </request>
+
+    <enum name="gravity" bitfield="true">
+      <entry name="none" value="0"
+	     summary="center over the anchor edge"/>
+      <entry name="top" value="1"
+	     summary="position above the anchor edge"/>
+      <entry name="bottom" value="2"
+	     summary="position below the anchor edge"/>
+      <entry name="left" value="4"
+	     summary="position to the left of the anchor edge"/>
+      <entry name="right" value="8"
+	     summary="position to the right of the anchor edge"/>
+    </enum>
+
+    <request name="set_gravity">
+      <description summary="set child surface gravity">
+	Defines in what direction a surface should be positioned, relative to
+	the anchor point of the parent surface. If two orthogonal gravities are
+	specified (e.g. 'bottom' and 'right'), then the child surface will be
+	placed in the specified direction; otherwise, the child surface will be
+	centered over the anchor point on any axis that had no gravity
+	specified.
+
+	If two parallel gravities are specified (e.g. 'left' and 'right'), the
+	invalid_input error is raised.
+      </description>
+      <arg name="gravity" type="uint" enum="gravity"
+	   summary="bit mask of gravity directions"/>
+    </request>
+
+    <enum name="constraint_adjustment" bitfield="true">
+      <description summary="constraint adjustments">
+	The constraint adjustment value define ways the compositor will adjust
+	the position of the surface, if the unadjusted position would result
+	in the surface being partly constrained.
+
+	Whether a surface is considered 'constrained' is left to the compositor
+	to determine. For example, the surface may be partly outside the
+	compositor's defined 'work area', thus necessitating the child surface's
+	position be adjusted until it is entirely inside the work area.
+
+	The adjustments can be combined, according to a defined precedence: 1)
+	Flip, 2) Slide, 3) Resize.
+      </description>
+      <entry name="none" value="0">
+	<description summary="don't move the child surface when constrained">
+	  Don't alter the surface position even if it is constrained on some
+	  axis, for example partially outside the edge of a monitor.
+	</description>
+      </entry>
+      <entry name="slide_x" value="1">
+	<description summary="move along the x axis until unconstrained">
+	  Slide the surface along the x axis until it is no longer constrained.
+
+	  First try to slide towards the direction of the gravity on the x axis
+	  until either the edge in the opposite direction of the gravity is
+	  unconstrained or the edge in the direction of the gravity is
+	  constrained.
+
+	  Then try to slide towards the opposite direction of the gravity on the
+	  x axis until either the edge in the direction of the gravity is
+	  unconstrained or the edge in the opposite direction of the gravity is
+	  constrained.
+	</description>
+      </entry>
+      <entry name="slide_y" value="2">
+	<description summary="move along the y axis until unconstrained">
+	  Slide the surface along the y axis until it is no longer constrained.
+
+	  First try to slide towards the direction of the gravity on the y axis
+	  until either the edge in the opposite direction of the gravity is
+	  unconstrained or the edge in the direction of the gravity is
+	  constrained.
+
+	  Then try to slide towards the opposite direction of the gravity on the
+	  y axis until either the edge in the direction of the gravity is
+	  unconstrained or the edge in the opposite direction of the gravity is
+	  constrained.
+	</description>
+      </entry>
+      <entry name="flip_x" value="4">
+	<description summary="invert the anchor and gravity on the x axis">
+	  Invert the anchor and gravity on the x axis if the surface is
+	  constrained on the x axis. For example, if the left edge of the
+	  surface is constrained, the gravity is 'left' and the anchor is
+	  'left', change the gravity to 'right' and the anchor to 'right'.
+
+	  If the adjusted position also ends up being constrained, the resulting
+	  position of the flip_x adjustment will be the one before the
+	  adjustment.
+	</description>
+      </entry>
+      <entry name="flip_y" value="8">
+	<description summary="invert the anchor and gravity on the y axis">
+	  Invert the anchor and gravity on the y axis if the surface is
+	  constrained on the y axis. For example, if the bottom edge of the
+	  surface is constrained, the gravity is 'bottom' and the anchor is
+	  'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+	  If the adjusted position also ends up being constrained, the resulting
+	  position of the flip_y adjustment will be the one before the
+	  adjustment.
+	</description>
+      </entry>
+      <entry name="resize_x" value="16">
+	<description summary="horizontally resize the surface">
+	  Resize the surface horizontally so that it is completely
+	  unconstrained.
+	</description>
+      </entry>
+      <entry name="resize_y" value="32">
+	<description summary="vertically resize the surface">
+	  Resize the surface vertically so that it is completely unconstrained.
+	</description>
+      </entry>
+    </enum>
+
+    <request name="set_constraint_adjustment">
+      <description summary="set the adjustment to be done when constrained">
+	Specify how the window should be positioned if the originally intended
+	position caused the surface to be constrained, meaning at least
+	partially outside positioning boundaries set by the compositor. The
+	adjustment is set by constructing a bitmask describing the adjustment to
+	be made when the surface is constrained on that axis.
+
+	If no bit for one axis is set, the compositor will assume that the child
+	surface should not change its position on that axis when constrained.
+
+	If more than one bit for one axis is set, the order of how adjustments
+	are applied is specified in the corresponding adjustment descriptions.
+
+	The default adjustment is none.
+      </description>
+      <arg name="constraint_adjustment" type="uint"
+	   summary="bit mask of constraint adjustments"/>
+    </request>
+
+    <request name="set_offset">
+      <description summary="set surface position offset">
+	Specify the surface position offset relative to the position of the
+	anchor on the anchor rectangle and the anchor on the surface. For
+	example if the anchor of the anchor rectangle is at (x, y), the surface
+	has the gravity bottom|right, and the offset is (ox, oy), the calculated
+	surface position will be (x + ox, y + oy). The offset position of the
+	surface is the one used for constraint testing. See
+	set_constraint_adjustment.
+
+	An example use case is placing a popup menu on top of a user interface
+	element, while aligning the user interface element of the parent surface
+	with some user interface element placed somewhere in the popup surface.
+      </description>
+      <arg name="x" type="int" summary="surface position x offset"/>
+      <arg name="y" type="int" summary="surface position y offset"/>
+    </request>
+  </interface>
+
+  <interface name="zxdg_surface_v6" version="1">
+    <description summary="desktop user interface surface base interface">
+      An interface that may be implemented by a wl_surface, for
+      implementations that provide a desktop-style user interface.
+
+      It provides a base set of functionality required to construct user
+      interface elements requiring management by the compositor, such as
+      toplevel windows, menus, etc. The types of functionality are split into
+      xdg_surface roles.
+
+      Creating an xdg_surface does not set the role for a wl_surface. In order
+      to map an xdg_surface, the client must create a role-specific object
+      using, e.g., get_toplevel, get_popup. The wl_surface for any given
+      xdg_surface can have at most one role, and may not be assigned any role
+      not based on xdg_surface.
+
+      A role must be assigned before any other requests are made to the
+      xdg_surface object.
+
+      The client must call wl_surface.commit on the corresponding wl_surface
+      for the xdg_surface state to take effect.
+
+      Creating an xdg_surface from a wl_surface which has a buffer attached or
+      committed is a client error, and any attempts by a client to attach or
+      manipulate a buffer prior to the first xdg_surface.configure call must
+      also be treated as errors.
+
+      For a surface to be mapped by the compositor, the following conditions
+      must be met: (1) the client has assigned a xdg_surface based role to the
+      surface, (2) the client has set and committed the xdg_surface state and
+      the role dependent state to the surface and (3) the client has committed a
+      buffer to the surface.
+    </description>
+
+    <enum name="error">
+      <entry name="not_constructed" value="1"/>
+      <entry name="already_constructed" value="2"/>
+      <entry name="unconfigured_buffer" value="3"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_surface">
+	Destroy the xdg_surface object. An xdg_surface must only be destroyed
+	after its role object has been destroyed.
+      </description>
+    </request>
+
+    <request name="get_toplevel">
+      <description summary="assign the xdg_toplevel surface role">
+	This creates an xdg_toplevel object for the given xdg_surface and gives
+	the associated wl_surface the xdg_toplevel role.
+
+	See the documentation of xdg_toplevel for more details about what an
+	xdg_toplevel is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="zxdg_toplevel_v6"/>
+    </request>
+
+    <request name="get_popup">
+      <description summary="assign the xdg_popup surface role">
+	This creates an xdg_popup object for the given xdg_surface and gives the
+	associated wl_surface the xdg_popup role.
+
+	See the documentation of xdg_popup for more details about what an
+	xdg_popup is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="zxdg_popup_v6"/>
+      <arg name="parent" type="object" interface="zxdg_surface_v6"/>
+      <arg name="positioner" type="object" interface="zxdg_positioner_v6"/>
+    </request>
+
+    <request name="set_window_geometry">
+      <description summary="set the new window geometry">
+	The window geometry of a surface is its "visible bounds" from the
+	user's perspective. Client-side decorations often have invisible
+	portions like drop-shadows which should be ignored for the
+	purposes of aligning, placing and constraining windows.
+
+	The window geometry is double buffered, and will be applied at the
+	time wl_surface.commit of the corresponding wl_surface is called.
+
+	Once the window geometry of the surface is set, it is not possible to
+	unset it, and it will remain the same until set_window_geometry is
+	called again, even if a new subsurface or buffer is attached.
+
+	If never set, the value is the full bounds of the surface,
+	including any subsurfaces. This updates dynamically on every
+	commit. This unset is meant for extremely simple clients.
+
+	The arguments are given in the surface-local coordinate space of
+	the wl_surface associated with this xdg_surface.
+
+	The width and height must be greater than zero. Setting an invalid size
+	will raise an error. When applied, the effective window geometry will be
+	the set window geometry clamped to the bounding rectangle of the
+	combined geometry of the surface of the xdg_surface and the associated
+	subsurfaces.
+      </description>
+      <arg name="x" type="int"/>
+      <arg name="y" type="int"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="ack_configure">
+      <description summary="ack a configure event">
+	When a configure event is received, if a client commits the
+	surface in response to the configure event, then the client
+	must make an ack_configure request sometime before the commit
+	request, passing along the serial of the configure event.
+
+	For instance, for toplevel surfaces the compositor might use this
+	information to move a surface to the top left only when the client has
+	drawn itself for the maximized or fullscreen state.
+
+	If the client receives multiple configure events before it
+	can respond to one, it only has to ack the last configure event.
+
+	A client is not required to commit immediately after sending
+	an ack_configure request - it may even ack_configure several times
+	before its next surface commit.
+
+	A client may send multiple ack_configure requests before committing, but
+	only the last request sent before a commit indicates which configure
+	event the client really is responding to.
+      </description>
+      <arg name="serial" type="uint" summary="the serial from the configure event"/>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+	The configure event marks the end of a configure sequence. A configure
+	sequence is a set of one or more events configuring the state of the
+	xdg_surface, including the final xdg_surface.configure event.
+
+	Where applicable, xdg_surface surface roles will during a configure
+	sequence extend this event as a latched state sent as events before the
+	xdg_surface.configure event. Such events should be considered to make up
+	a set of atomically applied configuration states, where the
+	xdg_surface.configure commits the accumulated state.
+
+	Clients should arrange their surface for the new states, and then send
+	an ack_configure request with the serial sent in this configure event at
+	some point before committing the new surface.
+
+	If the client receives multiple configure events before it can respond
+	to one, it is free to discard all but the last event it received.
+      </description>
+      <arg name="serial" type="uint" summary="serial of the configure event"/>
+    </event>
+  </interface>
+
+  <interface name="zxdg_toplevel_v6" version="1">
+    <description summary="toplevel surface">
+      This interface defines an xdg_surface role which allows a surface to,
+      among other things, set window-like properties such as maximize,
+      fullscreen, and minimize, set application-specific metadata like title and
+      id, and well as trigger user interactive operations such as interactive
+      resize and move.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_toplevel">
+	Unmap and destroy the window. The window will be effectively
+	hidden from the user's point of view, and all state like
+	maximization, fullscreen, and so on, will be lost.
+      </description>
+    </request>
+
+    <request name="set_parent">
+      <description summary="set the parent of this surface">
+	Set the "parent" of this surface. This window should be stacked
+	above a parent. The parent surface must be mapped as long as this
+	surface is mapped.
+
+	Parent windows should be set on dialogs, toolboxes, or other
+	"auxiliary" surfaces, so that the parent is raised when the dialog
+	is raised.
+      </description>
+      <arg name="parent" type="object" interface="zxdg_toplevel_v6" allow-null="true"/>
+    </request>
+
+    <request name="set_title">
+      <description summary="set surface title">
+	Set a short title for the surface.
+
+	This string may be used to identify the surface in a task bar,
+	window list, or other user interface elements provided by the
+	compositor.
+
+	The string must be encoded in UTF-8.
+      </description>
+      <arg name="title" type="string"/>
+    </request>
+
+    <request name="set_app_id">
+      <description summary="set application ID">
+	Set an application identifier for the surface.
+
+	The app ID identifies the general class of applications to which
+	the surface belongs. The compositor can use this to group multiple
+	surfaces together, or to determine how to launch a new application.
+
+	For D-Bus activatable applications, the app ID is used as the D-Bus
+	service name.
+
+	The compositor shell will try to group application surfaces together
+	by their app ID. As a best practice, it is suggested to select app
+	ID's that match the basename of the application's .desktop file.
+	For example, "org.freedesktop.FooViewer" where the .desktop file is
+	"org.freedesktop.FooViewer.desktop".
+
+	See the desktop-entry specification [0] for more details on
+	application identifiers and how they relate to well-known D-Bus
+	names and .desktop files.
+
+	[0] http://standards.freedesktop.org/desktop-entry-spec/
+      </description>
+      <arg name="app_id" type="string"/>
+    </request>
+
+    <request name="show_window_menu">
+      <description summary="show the window menu">
+	Clients implementing client-side decorations might want to show
+	a context menu when right-clicking on the decorations, giving the
+	user a menu that they can use to maximize or minimize the window.
+
+	This request asks the compositor to pop up such a window menu at
+	the given position, relative to the local surface coordinates of
+	the parent surface. There are no guarantees as to what menu items
+	the window menu contains.
+
+	This request must be used in response to some sort of user action
+	like a button press, key press, or touch down event.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+      <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
+      <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
+    </request>
+
+    <request name="move">
+      <description summary="start an interactive move">
+	Start an interactive, user-driven move of the surface.
+
+	This request must be used in response to some sort of user action
+	like a button press, key press, or touch down event. The passed
+	serial is used to determine the type of interactive move (touch,
+	pointer, etc).
+
+	The server may ignore move requests depending on the state of
+	the surface (e.g. fullscreen or maximized), or if the passed serial
+	is no longer valid.
+
+	If triggered, the surface will lose the focus of the device
+	(wl_pointer, wl_touch, etc) used for the move. It is up to the
+	compositor to visually indicate that the move is taking place, such as
+	updating a pointer cursor, during the move. There is no guarantee
+	that the device focus will return when the move is completed.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+    </request>
+
+    <enum name="resize_edge">
+      <description summary="edge values for resizing">
+	These values are used to indicate which edge of a surface
+	is being dragged in a resize operation.
+      </description>
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="right" value="8"/>
+      <entry name="top_right" value="9"/>
+      <entry name="bottom_right" value="10"/>
+    </enum>
+
+    <request name="resize">
+      <description summary="start an interactive resize">
+	Start a user-driven, interactive resize of the surface.
+
+	This request must be used in response to some sort of user action
+	like a button press, key press, or touch down event. The passed
+	serial is used to determine the type of interactive resize (touch,
+	pointer, etc).
+
+	The server may ignore resize requests depending on the state of
+	the surface (e.g. fullscreen or maximized).
+
+	If triggered, the client will receive configure events with the
+	"resize" state enum value and the expected sizes. See the "resize"
+	enum value for more details about what is required. The client
+	must also acknowledge configure events using "ack_configure". After
+	the resize is completed, the client will receive another "configure"
+	event without the resize state.
+
+	If triggered, the surface also will lose the focus of the device
+	(wl_pointer, wl_touch, etc) used for the resize. It is up to the
+	compositor to visually indicate that the resize is taking place,
+	such as updating a pointer cursor, during the resize. There is no
+	guarantee that the device focus will return when the resize is
+	completed.
+
+	The edges parameter specifies how the surface should be resized,
+	and is one of the values of the resize_edge enum. The compositor
+	may use this information to update the surface position for
+	example when dragging the top left corner. The compositor may also
+	use this information to adapt its behavior, e.g. choose an
+	appropriate cursor image.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+      <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
+    </request>
+
+    <enum name="state">
+      <description summary="types of state on the surface">
+	The different state values used on the surface. This is designed for
+	state values like maximized, fullscreen. It is paired with the
+	configure event to ensure that both the client and the compositor
+	setting the state can be synchronized.
+
+	States set in this way are double-buffered. They will get applied on
+	the next commit.
+      </description>
+      <entry name="maximized" value="1" summary="the surface is maximized">
+	<description summary="the surface is maximized">
+	  The surface is maximized. The window geometry specified in the configure
+	  event must be obeyed by the client.
+	</description>
+      </entry>
+      <entry name="fullscreen" value="2" summary="the surface is fullscreen">
+	<description summary="the surface is fullscreen">
+	  The surface is fullscreen. The window geometry specified in the configure
+	  event must be obeyed by the client.
+	</description>
+      </entry>
+      <entry name="resizing" value="3" summary="the surface is being resized">
+	<description summary="the surface is being resized">
+	  The surface is being resized. The window geometry specified in the
+	  configure event is a maximum; the client cannot resize beyond it.
+	  Clients that have aspect ratio or cell sizing configuration can use
+	  a smaller size, however.
+	</description>
+      </entry>
+      <entry name="activated" value="4" summary="the surface is now activated">
+	<description summary="the surface is now activated">
+	  Client window decorations should be painted as if the window is
+	  active. Do not assume this means that the window actually has
+	  keyboard or pointer focus.
+	</description>
+      </entry>
+    </enum>
+
+    <request name="set_max_size">
+      <description summary="set the maximum size">
+	Set a maximum size for the window.
+
+	The client can specify a maximum size so that the compositor does
+	not try to configure the window beyond this size.
+
+	The width and height arguments are in window geometry coordinates.
+	See xdg_surface.set_window_geometry.
+
+	Values set in this way are double-buffered. They will get applied
+	on the next commit.
+
+	The compositor can use this information to allow or disallow
+	different states like maximize or fullscreen and draw accurate
+	animations.
+
+	Similarly, a tiling window manager may use this information to
+	place and resize client windows in a more effective way.
+
+	The client should not rely on the compositor to obey the maximum
+	size. The compositor may decide to ignore the values set by the
+	client and request a larger size.
+
+	If never set, or a value of zero in the request, means that the
+	client has no expected maximum size in the given dimension.
+	As a result, a client wishing to reset the maximum size
+	to an unspecified state can use zero for width and height in the
+	request.
+
+	Requesting a maximum size to be smaller than the minimum size of
+	a surface is illegal and will result in a protocol error.
+
+	The width and height must be greater than or equal to zero. Using
+	strictly negative values for width and height will result in a
+	protocol error.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="set_min_size">
+      <description summary="set the minimum size">
+	Set a minimum size for the window.
+
+	The client can specify a minimum size so that the compositor does
+	not try to configure the window below this size.
+
+	The width and height arguments are in window geometry coordinates.
+	See xdg_surface.set_window_geometry.
+
+	Values set in this way are double-buffered. They will get applied
+	on the next commit.
+
+	The compositor can use this information to allow or disallow
+	different states like maximize or fullscreen and draw accurate
+	animations.
+
+	Similarly, a tiling window manager may use this information to
+	place and resize client windows in a more effective way.
+
+	The client should not rely on the compositor to obey the minimum
+	size. The compositor may decide to ignore the values set by the
+	client and request a smaller size.
+
+	If never set, or a value of zero in the request, means that the
+	client has no expected minimum size in the given dimension.
+	As a result, a client wishing to reset the minimum size
+	to an unspecified state can use zero for width and height in the
+	request.
+
+	Requesting a minimum size to be larger than the maximum size of
+	a surface is illegal and will result in a protocol error.
+
+	The width and height must be greater than or equal to zero. Using
+	strictly negative values for width and height will result in a
+	protocol error.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="set_maximized">
+      <description summary="maximize the window">
+	Maximize the surface.
+
+	After requesting that the surface should be maximized, the compositor
+	will respond by emitting a configure event with the "maximized" state
+	and the required window geometry. The client should then update its
+	content, drawing it in a maximized state, i.e. without shadow or other
+	decoration outside of the window geometry. The client must also
+	acknowledge the configure when committing the new content (see
+	ack_configure).
+
+	It is up to the compositor to decide how and where to maximize the
+	surface, for example which output and what region of the screen should
+	be used.
+
+	If the surface was already maximized, the compositor will still emit
+	a configure event with the "maximized" state.
+      </description>
+    </request>
+
+    <request name="unset_maximized">
+      <description summary="unmaximize the window">
+	Unmaximize the surface.
+
+	After requesting that the surface should be unmaximized, the compositor
+	will respond by emitting a configure event without the "maximized"
+	state. If available, the compositor will include the window geometry
+	dimensions the window had prior to being maximized in the configure
+	request. The client must then update its content, drawing it in a
+	regular state, i.e. potentially with shadow, etc. The client must also
+	acknowledge the configure when committing the new content (see
+	ack_configure).
+
+	It is up to the compositor to position the surface after it was
+	unmaximized; usually the position the surface had before maximizing, if
+	applicable.
+
+	If the surface was already not maximized, the compositor will still
+	emit a configure event without the "maximized" state.
+      </description>
+    </request>
+
+    <request name="set_fullscreen">
+      <description summary="set the window as fullscreen on a monitor">
+	Make the surface fullscreen.
+
+	You can specify an output that you would prefer to be fullscreen.
+	If this value is NULL, it's up to the compositor to choose which
+	display will be used to map this surface.
+
+	If the surface doesn't cover the whole output, the compositor will
+	position the surface in the center of the output and compensate with
+	black borders filling the rest of the output.
+      </description>
+      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+    </request>
+    <request name="unset_fullscreen" />
+
+    <request name="set_minimized">
+      <description summary="set the window as minimized">
+	Request that the compositor minimize your surface. There is no
+	way to know if the surface is currently minimized, nor is there
+	any way to unset minimization on this surface.
+
+	If you are looking to throttle redrawing when minimized, please
+	instead use the wl_surface.frame event for this, as this will
+	also work with live previews on windows in Alt-Tab, Expose or
+	similar compositor features.
+      </description>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+	This configure event asks the client to resize its toplevel surface or
+	to change its state. The configured state should not be applied
+	immediately. See xdg_surface.configure for details.
+
+	The width and height arguments specify a hint to the window
+	about how its surface should be resized in window geometry
+	coordinates. See set_window_geometry.
+
+	If the width or height arguments are zero, it means the client
+	should decide its own window dimension. This may happen when the
+	compositor needs to configure the state of the surface but doesn't
+	have any information about any previous or expected dimension.
+
+	The states listed in the event specify how the width/height
+	arguments should be interpreted, and possibly how it should be
+	drawn.
+
+	Clients must send an ack_configure in response to this event. See
+	xdg_surface.configure and xdg_surface.ack_configure for details.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="states" type="array"/>
+    </event>
+
+    <event name="close">
+      <description summary="surface wants to be closed">
+	The close event is sent by the compositor when the user
+	wants the surface to be closed. This should be equivalent to
+	the user clicking the close button in client-side decorations,
+	if your application has any.
+
+	This is only a request that the user intends to close the
+	window. The client may choose to ignore this request, or show
+	a dialog to ask the user to save their data, etc.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="zxdg_popup_v6" version="1">
+    <description summary="short-lived, popup surfaces for menus">
+      A popup surface is a short-lived, temporary surface. It can be used to
+      implement for example menus, popovers, tooltips and other similar user
+      interface concepts.
+
+      A popup can be made to take an explicit grab. See xdg_popup.grab for
+      details.
+
+      When the popup is dismissed, a popup_done event will be sent out, and at
+      the same time the surface will be unmapped. See the xdg_popup.popup_done
+      event for details.
+
+      Explicitly destroying the xdg_popup object will also dismiss the popup and
+      unmap the surface. Clients that want to dismiss the popup when another
+      surface of their own is clicked should dismiss the popup using the destroy
+      request.
+
+      The parent surface must have either the xdg_toplevel or xdg_popup surface
+      role.
+
+      A newly created xdg_popup will be stacked on top of all previously created
+      xdg_popup surfaces associated with the same xdg_toplevel.
+
+      The parent of an xdg_popup must be mapped (see the xdg_surface
+      description) before the xdg_popup itself.
+
+      The x and y arguments passed when creating the popup object specify
+      where the top left of the popup should be placed, relative to the
+      local surface coordinates of the parent surface. See
+      xdg_surface.get_popup. An xdg_popup must intersect with or be at least
+      partially adjacent to its parent surface.
+
+      The client must call wl_surface.commit on the corresponding wl_surface
+      for the xdg_popup state to take effect.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_grab" value="0"
+	     summary="tried to grab after being mapped"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="remove xdg_popup interface">
+	This destroys the popup. Explicitly destroying the xdg_popup
+	object will also dismiss the popup, and unmap the surface.
+
+	If this xdg_popup is not the "topmost" popup, a protocol error
+	will be sent.
+      </description>
+    </request>
+
+    <request name="grab">
+      <description summary="make the popup take an explicit grab">
+	This request makes the created popup take an explicit grab. An explicit
+	grab will be dismissed when the user dismisses the popup, or when the
+	client destroys the xdg_popup. This can be done by the user clicking
+	outside the surface, using the keyboard, or even locking the screen
+	through closing the lid or a timeout.
+
+	If the compositor denies the grab, the popup will be immediately
+	dismissed.
+
+	This request must be used in response to some sort of user action like a
+	button press, key press, or touch down event. The serial number of the
+	event should be passed as 'serial'.
+
+	The parent of a grabbing popup must either be an xdg_toplevel surface or
+	another xdg_popup with an explicit grab. If the parent is another
+	xdg_popup it means that the popups are nested, with this popup now being
+	the topmost popup.
+
+	Nested popups must be destroyed in the reverse order they were created
+	in, e.g. the only popup you are allowed to destroy at all times is the
+	topmost one.
+
+	When compositors choose to dismiss a popup, they may dismiss every
+	nested grabbing popup as well. When a compositor dismisses popups, it
+	will follow the same dismissing order as required from the client.
+
+	The parent of a grabbing popup must either be another xdg_popup with an
+	active explicit grab, or an xdg_popup or xdg_toplevel, if there are no
+	explicit grabs already taken.
+
+	If the topmost grabbing popup is destroyed, the grab will be returned to
+	the parent of the popup, if that parent previously had an explicit grab.
+
+	If the parent is a grabbing popup which has already been dismissed, this
+	popup will be immediately dismissed. If the parent is a popup that did
+	not take an explicit grab, an error will be raised.
+
+	During a popup grab, the client owning the grab will receive pointer
+	and touch events for all their surfaces as normal (similar to an
+	"owner-events" grab in X11 parlance), while the top most grabbing popup
+	will always have keyboard focus.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"
+	   summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+    </request>
+
+    <event name="configure">
+      <description summary="configure the popup surface">
+	This event asks the popup surface to configure itself given the
+	configuration. The configured state should not be applied immediately.
+	See xdg_surface.configure for details.
+
+	The x and y arguments represent the position the popup was placed at
+	given the xdg_positioner rule, relative to the upper left corner of the
+	window geometry of the parent surface.
+      </description>
+      <arg name="x" type="int"
+	   summary="x position relative to parent surface window geometry"/>
+      <arg name="y" type="int"
+	   summary="y position relative to parent surface window geometry"/>
+      <arg name="width" type="int" summary="window geometry width"/>
+      <arg name="height" type="int" summary="window geometry height"/>
+    </event>
+
+    <event name="popup_done">
+      <description summary="popup interaction is done">
+	The popup_done event is sent out when a popup is dismissed by the
+	compositor. The client should destroy the xdg_popup object at this
+	point.
+      </description>
+    </event>
+
+  </interface>
+</protocol>
diff --git a/source/wayland-protocols/xdg-shell.xml b/source/wayland-protocols/xdg-shell.xml
new file mode 100644
index 0000000..d524ea9
--- /dev/null
+++ b/source/wayland-protocols/xdg-shell.xml
@@ -0,0 +1,1120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_shell">
+
+  <copyright>
+    Copyright © 2008-2013 Kristian Høgsberg
+    Copyright © 2013      Rafael Antognolli
+    Copyright © 2013      Jasper St. Pierre
+    Copyright © 2010-2013 Intel Corporation
+    Copyright © 2015-2017 Samsung Electronics Co., Ltd
+    Copyright © 2015-2017 Red Hat Inc.
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="xdg_wm_base" version="1">
+    <description summary="create desktop-style surfaces">
+      The xdg_wm_base interface is exposed as a global object enabling clients
+      to turn their wl_surfaces into windows in a desktop environment. It
+      defines the basic functionality needed for clients and the compositor to
+      create windows that can be dragged, resized, maximized, etc, as well as
+      creating transient windows such as popup menus.
+    </description>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="given wl_surface has another role"/>
+      <entry name="defunct_surfaces" value="1"
+	     summary="xdg_wm_base was destroyed before children"/>
+      <entry name="not_the_topmost_popup" value="2"
+	     summary="the client tried to map or destroy a non-topmost popup"/>
+      <entry name="invalid_popup_parent" value="3"
+	     summary="the client specified an invalid popup parent surface"/>
+      <entry name="invalid_surface_state" value="4"
+	     summary="the client provided an invalid surface state"/>
+      <entry name="invalid_positioner" value="5"
+	     summary="the client provided an invalid positioner"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy xdg_wm_base">
+	Destroy this xdg_wm_base object.
+
+	Destroying a bound xdg_wm_base object while there are surfaces
+	still alive created by this xdg_wm_base object instance is illegal
+	and will result in a protocol error.
+      </description>
+    </request>
+
+    <request name="create_positioner">
+      <description summary="create a positioner object">
+	Create a positioner object. A positioner object is used to position
+	surfaces relative to some parent surface. See the interface description
+	and xdg_surface.get_popup for details.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_positioner"/>
+    </request>
+
+    <request name="get_xdg_surface">
+      <description summary="create a shell surface from a surface">
+	This creates an xdg_surface for the given surface. While xdg_surface
+	itself is not a role, the corresponding surface may only be assigned
+	a role extending xdg_surface, such as xdg_toplevel or xdg_popup.
+
+	This creates an xdg_surface for the given surface. An xdg_surface is
+	used as basis to define a role to a given surface, such as xdg_toplevel
+	or xdg_popup. It also manages functionality shared between xdg_surface
+	based surface roles.
+
+	See the documentation of xdg_surface for more details about what an
+	xdg_surface is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_surface"/>
+      <arg name="surface" type="object" interface="wl_surface"/>
+    </request>
+
+    <request name="pong">
+      <description summary="respond to a ping event">
+	A client must respond to a ping event with a pong request or
+	the client may be deemed unresponsive. See xdg_wm_base.ping.
+      </description>
+      <arg name="serial" type="uint" summary="serial of the ping event"/>
+    </request>
+
+    <event name="ping">
+      <description summary="check if the client is alive">
+	The ping event asks the client if it's still alive. Pass the
+	serial specified in the event back to the compositor by sending
+	a "pong" request back with the specified serial. See xdg_wm_base.ping.
+
+	Compositors can use this to determine if the client is still
+	alive. It's unspecified what will happen if the client doesn't
+	respond to the ping request, or in what timeframe. Clients should
+	try to respond in a reasonable amount of time.
+
+	A compositor is free to ping in any way it wants, but a client must
+	always respond to any xdg_wm_base object it created.
+      </description>
+      <arg name="serial" type="uint" summary="pass this to the pong request"/>
+    </event>
+  </interface>
+
+  <interface name="xdg_positioner" version="1">
+    <description summary="child surface positioner">
+      The xdg_positioner provides a collection of rules for the placement of a
+      child surface relative to a parent surface. Rules can be defined to ensure
+      the child surface remains within the visible area's borders, and to
+      specify how the child surface changes its position, such as sliding along
+      an axis, or flipping around a rectangle. These positioner-created rules are
+      constrained by the requirement that a child surface must intersect with or
+      be at least partially adjacent to its parent surface.
+
+      See the various requests for details about possible rules.
+
+      At the time of the request, the compositor makes a copy of the rules
+      specified by the xdg_positioner. Thus, after the request is complete the
+      xdg_positioner object can be destroyed or reused; further changes to the
+      object will have no effect on previous usages.
+
+      For an xdg_positioner object to be considered complete, it must have a
+      non-zero size set by set_size, and a non-zero anchor rectangle set by
+      set_anchor_rect. Passing an incomplete xdg_positioner object when
+      positioning a surface raises an error.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_input" value="0" summary="invalid input provided"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_positioner object">
+	Notify the compositor that the xdg_positioner will no longer be used.
+      </description>
+    </request>
+
+    <request name="set_size">
+      <description summary="set the size of the to-be positioned rectangle">
+	Set the size of the surface that is to be positioned with the positioner
+	object. The size is in surface-local coordinates and corresponds to the
+	window geometry. See xdg_surface.set_window_geometry.
+
+	If a zero or negative size is set the invalid_input error is raised.
+      </description>
+      <arg name="width" type="int" summary="width of positioned rectangle"/>
+      <arg name="height" type="int" summary="height of positioned rectangle"/>
+    </request>
+
+    <request name="set_anchor_rect">
+      <description summary="set the anchor rectangle within the parent surface">
+	Specify the anchor rectangle within the parent surface that the child
+	surface will be placed relative to. The rectangle is relative to the
+	window geometry as defined by xdg_surface.set_window_geometry of the
+	parent surface.
+
+	When the xdg_positioner object is used to position a child surface, the
+	anchor rectangle may not extend outside the window geometry of the
+	positioned child's parent surface.
+
+	If a negative size is set the invalid_input error is raised.
+      </description>
+      <arg name="x" type="int" summary="x position of anchor rectangle"/>
+      <arg name="y" type="int" summary="y position of anchor rectangle"/>
+      <arg name="width" type="int" summary="width of anchor rectangle"/>
+      <arg name="height" type="int" summary="height of anchor rectangle"/>
+    </request>
+
+    <enum name="anchor">
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="3"/>
+      <entry name="right" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="top_right" value="7"/>
+      <entry name="bottom_right" value="8"/>
+    </enum>
+
+    <request name="set_anchor">
+      <description summary="set anchor rectangle anchor">
+	Defines the anchor point for the anchor rectangle. The specified anchor
+	is used derive an anchor point that the child surface will be
+	positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+	'bottom_right'), the anchor point will be at the specified corner;
+	otherwise, the derived anchor point will be centered on the specified
+	edge, or in the center of the anchor rectangle if no edge is specified.
+      </description>
+      <arg name="anchor" type="uint" enum="anchor"
+	   summary="anchor"/>
+    </request>
+
+    <enum name="gravity">
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="3"/>
+      <entry name="right" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="top_right" value="7"/>
+      <entry name="bottom_right" value="8"/>
+    </enum>
+
+    <request name="set_gravity">
+      <description summary="set child surface gravity">
+	Defines in what direction a surface should be positioned, relative to
+	the anchor point of the parent surface. If a corner gravity is
+	specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+	will be placed towards the specified gravity; otherwise, the child
+	surface will be centered over the anchor point on any axis that had no
+	gravity specified.
+      </description>
+      <arg name="gravity" type="uint" enum="gravity"
+	   summary="gravity direction"/>
+    </request>
+
+    <enum name="constraint_adjustment" bitfield="true">
+      <description summary="constraint adjustments">
+	The constraint adjustment value define ways the compositor will adjust
+	the position of the surface, if the unadjusted position would result
+	in the surface being partly constrained.
+
+	Whether a surface is considered 'constrained' is left to the compositor
+	to determine. For example, the surface may be partly outside the
+	compositor's defined 'work area', thus necessitating the child surface's
+	position be adjusted until it is entirely inside the work area.
+
+	The adjustments can be combined, according to a defined precedence: 1)
+	Flip, 2) Slide, 3) Resize.
+      </description>
+      <entry name="none" value="0">
+	<description summary="don't move the child surface when constrained">
+	  Don't alter the surface position even if it is constrained on some
+	  axis, for example partially outside the edge of an output.
+	</description>
+      </entry>
+      <entry name="slide_x" value="1">
+	<description summary="move along the x axis until unconstrained">
+	  Slide the surface along the x axis until it is no longer constrained.
+
+	  First try to slide towards the direction of the gravity on the x axis
+	  until either the edge in the opposite direction of the gravity is
+	  unconstrained or the edge in the direction of the gravity is
+	  constrained.
+
+	  Then try to slide towards the opposite direction of the gravity on the
+	  x axis until either the edge in the direction of the gravity is
+	  unconstrained or the edge in the opposite direction of the gravity is
+	  constrained.
+	</description>
+      </entry>
+      <entry name="slide_y" value="2">
+	<description summary="move along the y axis until unconstrained">
+	  Slide the surface along the y axis until it is no longer constrained.
+
+	  First try to slide towards the direction of the gravity on the y axis
+	  until either the edge in the opposite direction of the gravity is
+	  unconstrained or the edge in the direction of the gravity is
+	  constrained.
+
+	  Then try to slide towards the opposite direction of the gravity on the
+	  y axis until either the edge in the direction of the gravity is
+	  unconstrained or the edge in the opposite direction of the gravity is
+	  constrained.
+	</description>
+      </entry>
+      <entry name="flip_x" value="4">
+	<description summary="invert the anchor and gravity on the x axis">
+	  Invert the anchor and gravity on the x axis if the surface is
+	  constrained on the x axis. For example, if the left edge of the
+	  surface is constrained, the gravity is 'left' and the anchor is
+	  'left', change the gravity to 'right' and the anchor to 'right'.
+
+	  If the adjusted position also ends up being constrained, the resulting
+	  position of the flip_x adjustment will be the one before the
+	  adjustment.
+	</description>
+      </entry>
+      <entry name="flip_y" value="8">
+	<description summary="invert the anchor and gravity on the y axis">
+	  Invert the anchor and gravity on the y axis if the surface is
+	  constrained on the y axis. For example, if the bottom edge of the
+	  surface is constrained, the gravity is 'bottom' and the anchor is
+	  'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+	  The adjusted position is calculated given the original anchor
+	  rectangle and offset, but with the new flipped anchor and gravity
+	  values.
+
+	  If the adjusted position also ends up being constrained, the resulting
+	  position of the flip_y adjustment will be the one before the
+	  adjustment.
+	</description>
+      </entry>
+      <entry name="resize_x" value="16">
+	<description summary="horizontally resize the surface">
+	  Resize the surface horizontally so that it is completely
+	  unconstrained.
+	</description>
+      </entry>
+      <entry name="resize_y" value="32">
+	<description summary="vertically resize the surface">
+	  Resize the surface vertically so that it is completely unconstrained.
+	</description>
+      </entry>
+    </enum>
+
+    <request name="set_constraint_adjustment">
+      <description summary="set the adjustment to be done when constrained">
+	Specify how the window should be positioned if the originally intended
+	position caused the surface to be constrained, meaning at least
+	partially outside positioning boundaries set by the compositor. The
+	adjustment is set by constructing a bitmask describing the adjustment to
+	be made when the surface is constrained on that axis.
+
+	If no bit for one axis is set, the compositor will assume that the child
+	surface should not change its position on that axis when constrained.
+
+	If more than one bit for one axis is set, the order of how adjustments
+	are applied is specified in the corresponding adjustment descriptions.
+
+	The default adjustment is none.
+      </description>
+      <arg name="constraint_adjustment" type="uint"
+	   summary="bit mask of constraint adjustments"/>
+    </request>
+
+    <request name="set_offset">
+      <description summary="set surface position offset">
+	Specify the surface position offset relative to the position of the
+	anchor on the anchor rectangle and the anchor on the surface. For
+	example if the anchor of the anchor rectangle is at (x, y), the surface
+	has the gravity bottom|right, and the offset is (ox, oy), the calculated
+	surface position will be (x + ox, y + oy). The offset position of the
+	surface is the one used for constraint testing. See
+	set_constraint_adjustment.
+
+	An example use case is placing a popup menu on top of a user interface
+	element, while aligning the user interface element of the parent surface
+	with some user interface element placed somewhere in the popup surface.
+      </description>
+      <arg name="x" type="int" summary="surface position x offset"/>
+      <arg name="y" type="int" summary="surface position y offset"/>
+    </request>
+  </interface>
+
+  <interface name="xdg_surface" version="1">
+    <description summary="desktop user interface surface base interface">
+      An interface that may be implemented by a wl_surface, for
+      implementations that provide a desktop-style user interface.
+
+      It provides a base set of functionality required to construct user
+      interface elements requiring management by the compositor, such as
+      toplevel windows, menus, etc. The types of functionality are split into
+      xdg_surface roles.
+
+      Creating an xdg_surface does not set the role for a wl_surface. In order
+      to map an xdg_surface, the client must create a role-specific object
+      using, e.g., get_toplevel, get_popup. The wl_surface for any given
+      xdg_surface can have at most one role, and may not be assigned any role
+      not based on xdg_surface.
+
+      A role must be assigned before any other requests are made to the
+      xdg_surface object.
+
+      The client must call wl_surface.commit on the corresponding wl_surface
+      for the xdg_surface state to take effect.
+
+      Creating an xdg_surface from a wl_surface which has a buffer attached or
+      committed is a client error, and any attempts by a client to attach or
+      manipulate a buffer prior to the first xdg_surface.configure call must
+      also be treated as errors.
+
+      Mapping an xdg_surface-based role surface is defined as making it
+      possible for the surface to be shown by the compositor. Note that
+      a mapped surface is not guaranteed to be visible once it is mapped.
+
+      For an xdg_surface to be mapped by the compositor, the following
+      conditions must be met:
+      (1) the client has assigned an xdg_surface-based role to the surface
+      (2) the client has set and committed the xdg_surface state and the
+	  role-dependent state to the surface
+      (3) the client has committed a buffer to the surface
+
+      A newly-unmapped surface is considered to have met condition (1) out
+      of the 3 required conditions for mapping a surface if its role surface
+      has not been destroyed.
+    </description>
+
+    <enum name="error">
+      <entry name="not_constructed" value="1"/>
+      <entry name="already_constructed" value="2"/>
+      <entry name="unconfigured_buffer" value="3"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_surface">
+	Destroy the xdg_surface object. An xdg_surface must only be destroyed
+	after its role object has been destroyed.
+      </description>
+    </request>
+
+    <request name="get_toplevel">
+      <description summary="assign the xdg_toplevel surface role">
+	This creates an xdg_toplevel object for the given xdg_surface and gives
+	the associated wl_surface the xdg_toplevel role.
+
+	See the documentation of xdg_toplevel for more details about what an
+	xdg_toplevel is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_toplevel"/>
+    </request>
+
+    <request name="get_popup">
+      <description summary="assign the xdg_popup surface role">
+	This creates an xdg_popup object for the given xdg_surface and gives
+	the associated wl_surface the xdg_popup role.
+
+	If null is passed as a parent, a parent surface must be specified using
+	some other protocol, before committing the initial state.
+
+	See the documentation of xdg_popup for more details about what an
+	xdg_popup is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_popup"/>
+      <arg name="parent" type="object" interface="xdg_surface" allow-null="true"/>
+      <arg name="positioner" type="object" interface="xdg_positioner"/>
+    </request>
+
+    <request name="set_window_geometry">
+      <description summary="set the new window geometry">
+	The window geometry of a surface is its "visible bounds" from the
+	user's perspective. Client-side decorations often have invisible
+	portions like drop-shadows which should be ignored for the
+	purposes of aligning, placing and constraining windows.
+
+	The window geometry is double buffered, and will be applied at the
+	time wl_surface.commit of the corresponding wl_surface is called.
+
+	When maintaining a position, the compositor should treat the (x, y)
+	coordinate of the window geometry as the top left corner of the window.
+	A client changing the (x, y) window geometry coordinate should in
+	general not alter the position of the window.
+
+	Once the window geometry of the surface is set, it is not possible to
+	unset it, and it will remain the same until set_window_geometry is
+	called again, even if a new subsurface or buffer is attached.
+
+	If never set, the value is the full bounds of the surface,
+	including any subsurfaces. This updates dynamically on every
+	commit. This unset is meant for extremely simple clients.
+
+	The arguments are given in the surface-local coordinate space of
+	the wl_surface associated with this xdg_surface.
+
+	The width and height must be greater than zero. Setting an invalid size
+	will raise an error. When applied, the effective window geometry will be
+	the set window geometry clamped to the bounding rectangle of the
+	combined geometry of the surface of the xdg_surface and the associated
+	subsurfaces.
+      </description>
+      <arg name="x" type="int"/>
+      <arg name="y" type="int"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="ack_configure">
+      <description summary="ack a configure event">
+	When a configure event is received, if a client commits the
+	surface in response to the configure event, then the client
+	must make an ack_configure request sometime before the commit
+	request, passing along the serial of the configure event.
+
+	For instance, for toplevel surfaces the compositor might use this
+	information to move a surface to the top left only when the client has
+	drawn itself for the maximized or fullscreen state.
+
+	If the client receives multiple configure events before it
+	can respond to one, it only has to ack the last configure event.
+
+	A client is not required to commit immediately after sending
+	an ack_configure request - it may even ack_configure several times
+	before its next surface commit.
+
+	A client may send multiple ack_configure requests before committing, but
+	only the last request sent before a commit indicates which configure
+	event the client really is responding to.
+      </description>
+      <arg name="serial" type="uint" summary="the serial from the configure event"/>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+	The configure event marks the end of a configure sequence. A configure
+	sequence is a set of one or more events configuring the state of the
+	xdg_surface, including the final xdg_surface.configure event.
+
+	Where applicable, xdg_surface surface roles will during a configure
+	sequence extend this event as a latched state sent as events before the
+	xdg_surface.configure event. Such events should be considered to make up
+	a set of atomically applied configuration states, where the
+	xdg_surface.configure commits the accumulated state.
+
+	Clients should arrange their surface for the new states, and then send
+	an ack_configure request with the serial sent in this configure event at
+	some point before committing the new surface.
+
+	If the client receives multiple configure events before it can respond
+	to one, it is free to discard all but the last event it received.
+      </description>
+      <arg name="serial" type="uint" summary="serial of the configure event"/>
+    </event>
+  </interface>
+
+  <interface name="xdg_toplevel" version="1">
+    <description summary="toplevel surface">
+      This interface defines an xdg_surface role which allows a surface to,
+      among other things, set window-like properties such as maximize,
+      fullscreen, and minimize, set application-specific metadata like title and
+      id, and well as trigger user interactive operations such as interactive
+      resize and move.
+
+      Unmapping an xdg_toplevel means that the surface cannot be shown
+      by the compositor until it is explicitly mapped again.
+      All active operations (e.g., move, resize) are canceled and all
+      attributes (e.g. title, state, stacking, ...) are discarded for
+      an xdg_toplevel surface when it is unmapped.
+
+      Attaching a null buffer to a toplevel unmaps the surface.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_toplevel">
+	This request destroys the role surface and unmaps the surface;
+	see "Unmapping" behavior in interface section for details.
+      </description>
+    </request>
+
+    <request name="set_parent">
+      <description summary="set the parent of this surface">
+	Set the "parent" of this surface. This surface should be stacked
+	above the parent surface and all other ancestor surfaces.
+
+	Parent windows should be set on dialogs, toolboxes, or other
+	"auxiliary" surfaces, so that the parent is raised when the dialog
+	is raised.
+
+	Setting a null parent for a child window removes any parent-child
+	relationship for the child. Setting a null parent for a window which
+	currently has no parent is a no-op.
+
+	If the parent is unmapped then its children are managed as
+	though the parent of the now-unmapped parent has become the
+	parent of this surface. If no parent exists for the now-unmapped
+	parent then the children are managed as though they have no
+	parent surface.
+      </description>
+      <arg name="parent" type="object" interface="xdg_toplevel" allow-null="true"/>
+    </request>
+
+    <request name="set_title">
+      <description summary="set surface title">
+	Set a short title for the surface.
+
+	This string may be used to identify the surface in a task bar,
+	window list, or other user interface elements provided by the
+	compositor.
+
+	The string must be encoded in UTF-8.
+      </description>
+      <arg name="title" type="string"/>
+    </request>
+
+    <request name="set_app_id">
+      <description summary="set application ID">
+	Set an application identifier for the surface.
+
+	The app ID identifies the general class of applications to which
+	the surface belongs. The compositor can use this to group multiple
+	surfaces together, or to determine how to launch a new application.
+
+	For D-Bus activatable applications, the app ID is used as the D-Bus
+	service name.
+
+	The compositor shell will try to group application surfaces together
+	by their app ID. As a best practice, it is suggested to select app
+	ID's that match the basename of the application's .desktop file.
+	For example, "org.freedesktop.FooViewer" where the .desktop file is
+	"org.freedesktop.FooViewer.desktop".
+
+	See the desktop-entry specification [0] for more details on
+	application identifiers and how they relate to well-known D-Bus
+	names and .desktop files.
+
+	[0] http://standards.freedesktop.org/desktop-entry-spec/
+      </description>
+      <arg name="app_id" type="string"/>
+    </request>
+
+    <request name="show_window_menu">
+      <description summary="show the window menu">
+	Clients implementing client-side decorations might want to show
+	a context menu when right-clicking on the decorations, giving the
+	user a menu that they can use to maximize or minimize the window.
+
+	This request asks the compositor to pop up such a window menu at
+	the given position, relative to the local surface coordinates of
+	the parent surface. There are no guarantees as to what menu items
+	the window menu contains.
+
+	This request must be used in response to some sort of user action
+	like a button press, key press, or touch down event.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+      <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
+      <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
+    </request>
+
+    <request name="move">
+      <description summary="start an interactive move">
+	Start an interactive, user-driven move of the surface.
+
+	This request must be used in response to some sort of user action
+	like a button press, key press, or touch down event. The passed
+	serial is used to determine the type of interactive move (touch,
+	pointer, etc).
+
+	The server may ignore move requests depending on the state of
+	the surface (e.g. fullscreen or maximized), or if the passed serial
+	is no longer valid.
+
+	If triggered, the surface will lose the focus of the device
+	(wl_pointer, wl_touch, etc) used for the move. It is up to the
+	compositor to visually indicate that the move is taking place, such as
+	updating a pointer cursor, during the move. There is no guarantee
+	that the device focus will return when the move is completed.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+    </request>
+
+    <enum name="resize_edge">
+      <description summary="edge values for resizing">
+	These values are used to indicate which edge of a surface
+	is being dragged in a resize operation.
+      </description>
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="right" value="8"/>
+      <entry name="top_right" value="9"/>
+      <entry name="bottom_right" value="10"/>
+    </enum>
+
+    <request name="resize">
+      <description summary="start an interactive resize">
+	Start a user-driven, interactive resize of the surface.
+
+	This request must be used in response to some sort of user action
+	like a button press, key press, or touch down event. The passed
+	serial is used to determine the type of interactive resize (touch,
+	pointer, etc).
+
+	The server may ignore resize requests depending on the state of
+	the surface (e.g. fullscreen or maximized).
+
+	If triggered, the client will receive configure events with the
+	"resize" state enum value and the expected sizes. See the "resize"
+	enum value for more details about what is required. The client
+	must also acknowledge configure events using "ack_configure". After
+	the resize is completed, the client will receive another "configure"
+	event without the resize state.
+
+	If triggered, the surface also will lose the focus of the device
+	(wl_pointer, wl_touch, etc) used for the resize. It is up to the
+	compositor to visually indicate that the resize is taking place,
+	such as updating a pointer cursor, during the resize. There is no
+	guarantee that the device focus will return when the resize is
+	completed.
+
+	The edges parameter specifies how the surface should be resized,
+	and is one of the values of the resize_edge enum. The compositor
+	may use this information to update the surface position for
+	example when dragging the top left corner. The compositor may also
+	use this information to adapt its behavior, e.g. choose an
+	appropriate cursor image.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+      <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
+    </request>
+
+    <enum name="state">
+      <description summary="types of state on the surface">
+	The different state values used on the surface. This is designed for
+	state values like maximized, fullscreen. It is paired with the
+	configure event to ensure that both the client and the compositor
+	setting the state can be synchronized.
+
+	States set in this way are double-buffered. They will get applied on
+	the next commit.
+      </description>
+      <entry name="maximized" value="1" summary="the surface is maximized">
+	<description summary="the surface is maximized">
+	  The surface is maximized. The window geometry specified in the configure
+	  event must be obeyed by the client.
+	</description>
+      </entry>
+      <entry name="fullscreen" value="2" summary="the surface is fullscreen">
+	<description summary="the surface is fullscreen">
+	  The surface is fullscreen. The window geometry specified in the
+	  configure event is a maximum; the client cannot resize beyond it. For
+	  a surface to cover the whole fullscreened area, the geometry
+	  dimensions must be obeyed by the client. For more details, see
+	  xdg_toplevel.set_fullscreen.
+	</description>
+      </entry>
+      <entry name="resizing" value="3" summary="the surface is being resized">
+	<description summary="the surface is being resized">
+	  The surface is being resized. The window geometry specified in the
+	  configure event is a maximum; the client cannot resize beyond it.
+	  Clients that have aspect ratio or cell sizing configuration can use
+	  a smaller size, however.
+	</description>
+      </entry>
+      <entry name="activated" value="4" summary="the surface is now activated">
+	<description summary="the surface is now activated">
+	  Client window decorations should be painted as if the window is
+	  active. Do not assume this means that the window actually has
+	  keyboard or pointer focus.
+	</description>
+      </entry>
+    </enum>
+
+    <request name="set_max_size">
+      <description summary="set the maximum size">
+	Set a maximum size for the window.
+
+	The client can specify a maximum size so that the compositor does
+	not try to configure the window beyond this size.
+
+	The width and height arguments are in window geometry coordinates.
+	See xdg_surface.set_window_geometry.
+
+	Values set in this way are double-buffered. They will get applied
+	on the next commit.
+
+	The compositor can use this information to allow or disallow
+	different states like maximize or fullscreen and draw accurate
+	animations.
+
+	Similarly, a tiling window manager may use this information to
+	place and resize client windows in a more effective way.
+
+	The client should not rely on the compositor to obey the maximum
+	size. The compositor may decide to ignore the values set by the
+	client and request a larger size.
+
+	If never set, or a value of zero in the request, means that the
+	client has no expected maximum size in the given dimension.
+	As a result, a client wishing to reset the maximum size
+	to an unspecified state can use zero for width and height in the
+	request.
+
+	Requesting a maximum size to be smaller than the minimum size of
+	a surface is illegal and will result in a protocol error.
+
+	The width and height must be greater than or equal to zero. Using
+	strictly negative values for width and height will result in a
+	protocol error.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="set_min_size">
+      <description summary="set the minimum size">
+	Set a minimum size for the window.
+
+	The client can specify a minimum size so that the compositor does
+	not try to configure the window below this size.
+
+	The width and height arguments are in window geometry coordinates.
+	See xdg_surface.set_window_geometry.
+
+	Values set in this way are double-buffered. They will get applied
+	on the next commit.
+
+	The compositor can use this information to allow or disallow
+	different states like maximize or fullscreen and draw accurate
+	animations.
+
+	Similarly, a tiling window manager may use this information to
+	place and resize client windows in a more effective way.
+
+	The client should not rely on the compositor to obey the minimum
+	size. The compositor may decide to ignore the values set by the
+	client and request a smaller size.
+
+	If never set, or a value of zero in the request, means that the
+	client has no expected minimum size in the given dimension.
+	As a result, a client wishing to reset the minimum size
+	to an unspecified state can use zero for width and height in the
+	request.
+
+	Requesting a minimum size to be larger than the maximum size of
+	a surface is illegal and will result in a protocol error.
+
+	The width and height must be greater than or equal to zero. Using
+	strictly negative values for width and height will result in a
+	protocol error.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="set_maximized">
+      <description summary="maximize the window">
+	Maximize the surface.
+
+	After requesting that the surface should be maximized, the compositor
+	will respond by emitting a configure event with the "maximized" state
+	and the required window geometry. The client should then update its
+	content, drawing it in a maximized state, i.e. without shadow or other
+	decoration outside of the window geometry. The client must also
+	acknowledge the configure when committing the new content (see
+	ack_configure).
+
+	It is up to the compositor to decide how and where to maximize the
+	surface, for example which output and what region of the screen should
+	be used.
+
+	If the surface was already maximized, the compositor will still emit
+	a configure event with the "maximized" state.
+
+	If the surface is in a fullscreen state, this request has no direct
+	effect. It will alter the state the surface is returned to when
+	unmaximized if not overridden by the compositor.
+      </description>
+    </request>
+
+    <request name="unset_maximized">
+      <description summary="unmaximize the window">
+	Unmaximize the surface.
+
+	After requesting that the surface should be unmaximized, the compositor
+	will respond by emitting a configure event without the "maximized"
+	state. If available, the compositor will include the window geometry
+	dimensions the window had prior to being maximized in the configure
+	event. The client must then update its content, drawing it in a
+	regular state, i.e. potentially with shadow, etc. The client must also
+	acknowledge the configure when committing the new content (see
+	ack_configure).
+
+	It is up to the compositor to position the surface after it was
+	unmaximized; usually the position the surface had before maximizing, if
+	applicable.
+
+	If the surface was already not maximized, the compositor will still
+	emit a configure event without the "maximized" state.
+
+	If the surface is in a fullscreen state, this request has no direct
+	effect. It will alter the state the surface is returned to when
+	unmaximized if not overridden by the compositor.
+      </description>
+    </request>
+
+    <request name="set_fullscreen">
+      <description summary="set the window as fullscreen on an output">
+	Make the surface fullscreen.
+
+	After requesting that the surface should be fullscreened, the
+	compositor will respond by emitting a configure event with the
+	"fullscreen" state and the fullscreen window geometry. The client must
+	also acknowledge the configure when committing the new content (see
+	ack_configure).
+
+	The output passed by the request indicates the client's preference as
+	to which display it should be set fullscreen on. If this value is NULL,
+	it's up to the compositor to choose which display will be used to map
+	this surface.
+
+	If the surface doesn't cover the whole output, the compositor will
+	position the surface in the center of the output and compensate with
+	with border fill covering the rest of the output. The content of the
+	border fill is undefined, but should be assumed to be in some way that
+	attempts to blend into the surrounding area (e.g. solid black).
+
+	If the fullscreened surface is not opaque, the compositor must make
+	sure that other screen content not part of the same surface tree (made
+	up of subsurfaces, popups or similarly coupled surfaces) are not
+	visible below the fullscreened surface.
+      </description>
+      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+    </request>
+
+    <request name="unset_fullscreen">
+      <description summary="unset the window as fullscreen">
+	Make the surface no longer fullscreen.
+
+	After requesting that the surface should be unfullscreened, the
+	compositor will respond by emitting a configure event without the
+	"fullscreen" state.
+
+	Making a surface unfullscreen sets states for the surface based on the following:
+	* the state(s) it may have had before becoming fullscreen
+	* any state(s) decided by the compositor
+	* any state(s) requested by the client while the surface was fullscreen
+
+	The compositor may include the previous window geometry dimensions in
+	the configure event, if applicable.
+
+	The client must also acknowledge the configure when committing the new
+	content (see ack_configure).
+      </description>
+    </request>
+
+    <request name="set_minimized">
+      <description summary="set the window as minimized">
+	Request that the compositor minimize your surface. There is no
+	way to know if the surface is currently minimized, nor is there
+	any way to unset minimization on this surface.
+
+	If you are looking to throttle redrawing when minimized, please
+	instead use the wl_surface.frame event for this, as this will
+	also work with live previews on windows in Alt-Tab, Expose or
+	similar compositor features.
+      </description>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+	This configure event asks the client to resize its toplevel surface or
+	to change its state. The configured state should not be applied
+	immediately. See xdg_surface.configure for details.
+
+	The width and height arguments specify a hint to the window
+	about how its surface should be resized in window geometry
+	coordinates. See set_window_geometry.
+
+	If the width or height arguments are zero, it means the client
+	should decide its own window dimension. This may happen when the
+	compositor needs to configure the state of the surface but doesn't
+	have any information about any previous or expected dimension.
+
+	The states listed in the event specify how the width/height
+	arguments should be interpreted, and possibly how it should be
+	drawn.
+
+	Clients must send an ack_configure in response to this event. See
+	xdg_surface.configure and xdg_surface.ack_configure for details.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="states" type="array"/>
+    </event>
+
+    <event name="close">
+      <description summary="surface wants to be closed">
+	The close event is sent by the compositor when the user
+	wants the surface to be closed. This should be equivalent to
+	the user clicking the close button in client-side decorations,
+	if your application has any.
+
+	This is only a request that the user intends to close the
+	window. The client may choose to ignore this request, or show
+	a dialog to ask the user to save their data, etc.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="xdg_popup" version="1">
+    <description summary="short-lived, popup surfaces for menus">
+      A popup surface is a short-lived, temporary surface. It can be used to
+      implement for example menus, popovers, tooltips and other similar user
+      interface concepts.
+
+      A popup can be made to take an explicit grab. See xdg_popup.grab for
+      details.
+
+      When the popup is dismissed, a popup_done event will be sent out, and at
+      the same time the surface will be unmapped. See the xdg_popup.popup_done
+      event for details.
+
+      Explicitly destroying the xdg_popup object will also dismiss the popup and
+      unmap the surface. Clients that want to dismiss the popup when another
+      surface of their own is clicked should dismiss the popup using the destroy
+      request.
+
+      The parent surface must have either the xdg_toplevel or xdg_popup surface
+      role.
+
+      A newly created xdg_popup will be stacked on top of all previously created
+      xdg_popup surfaces associated with the same xdg_toplevel.
+
+      The parent of an xdg_popup must be mapped (see the xdg_surface
+      description) before the xdg_popup itself.
+
+      The x and y arguments passed when creating the popup object specify
+      where the top left of the popup should be placed, relative to the
+      local surface coordinates of the parent surface. See
+      xdg_surface.get_popup. An xdg_popup must intersect with or be at least
+      partially adjacent to its parent surface.
+
+      The client must call wl_surface.commit on the corresponding wl_surface
+      for the xdg_popup state to take effect.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_grab" value="0"
+	     summary="tried to grab after being mapped"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="remove xdg_popup interface">
+	This destroys the popup. Explicitly destroying the xdg_popup
+	object will also dismiss the popup, and unmap the surface.
+
+	If this xdg_popup is not the "topmost" popup, a protocol error
+	will be sent.
+      </description>
+    </request>
+
+    <request name="grab">
+      <description summary="make the popup take an explicit grab">
+	This request makes the created popup take an explicit grab. An explicit
+	grab will be dismissed when the user dismisses the popup, or when the
+	client destroys the xdg_popup. This can be done by the user clicking
+	outside the surface, using the keyboard, or even locking the screen
+	through closing the lid or a timeout.
+
+	If the compositor denies the grab, the popup will be immediately
+	dismissed.
+
+	This request must be used in response to some sort of user action like a
+	button press, key press, or touch down event. The serial number of the
+	event should be passed as 'serial'.
+
+	The parent of a grabbing popup must either be an xdg_toplevel surface or
+	another xdg_popup with an explicit grab. If the parent is another
+	xdg_popup it means that the popups are nested, with this popup now being
+	the topmost popup.
+
+	Nested popups must be destroyed in the reverse order they were created
+	in, e.g. the only popup you are allowed to destroy at all times is the
+	topmost one.
+
+	When compositors choose to dismiss a popup, they may dismiss every
+	nested grabbing popup as well. When a compositor dismisses popups, it
+	will follow the same dismissing order as required from the client.
+
+	The parent of a grabbing popup must either be another xdg_popup with an
+	active explicit grab, or an xdg_popup or xdg_toplevel, if there are no
+	explicit grabs already taken.
+
+	If the topmost grabbing popup is destroyed, the grab will be returned to
+	the parent of the popup, if that parent previously had an explicit grab.
+
+	If the parent is a grabbing popup which has already been dismissed, this
+	popup will be immediately dismissed. If the parent is a popup that did
+	not take an explicit grab, an error will be raised.
+
+	During a popup grab, the client owning the grab will receive pointer
+	and touch events for all their surfaces as normal (similar to an
+	"owner-events" grab in X11 parlance), while the top most grabbing popup
+	will always have keyboard focus.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"
+	   summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+    </request>
+
+    <event name="configure">
+      <description summary="configure the popup surface">
+	This event asks the popup surface to configure itself given the
+	configuration. The configured state should not be applied immediately.
+	See xdg_surface.configure for details.
+
+	The x and y arguments represent the position the popup was placed at
+	given the xdg_positioner rule, relative to the upper left corner of the
+	window geometry of the parent surface.
+      </description>
+      <arg name="x" type="int"
+	   summary="x position relative to parent surface window geometry"/>
+      <arg name="y" type="int"
+	   summary="y position relative to parent surface window geometry"/>
+      <arg name="width" type="int" summary="window geometry width"/>
+      <arg name="height" type="int" summary="window geometry height"/>
+    </event>
+
+    <event name="popup_done">
+      <description summary="popup interaction is done">
+	The popup_done event is sent out when a popup is dismissed by the
+	compositor. The client should destroy the xdg_popup object at this
+	point.
+      </description>
+    </event>
+
+  </interface>
+</protocol>

--
Gitblit v1.9.3