Browse Source

First pass on the new SDL sensor API

Sam Lantinga 6 years ago
parent
commit
7c3040e08a

+ 1 - 0
.hgignore

@@ -112,6 +112,7 @@ test/testresample
 test/testrumble
 test/testscale
 test/testsem
+test/testsensor
 test/testshader
 test/testshape
 test/testsprite2

+ 2 - 0
Android.mk

@@ -36,6 +36,8 @@ LOCAL_SRC_FILES := \
 	$(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) \

+ 4 - 0
CMakeLists.txt

@@ -750,6 +750,10 @@ if(SDL_HAPTIC)
   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)
   set(SOURCE_FILES ${SOURCE_FILES} ${POWER_SOURCES})

+ 1 - 0
Makefile.in

@@ -101,6 +101,7 @@ HDRS = \
 	SDL_render.h \
 	SDL_rwops.h \
 	SDL_scancode.h \
+	SDL_sensor.h \
 	SDL_shape.h \
 	SDL_stdinc.h \
 	SDL_surface.h \

+ 1 - 0
Makefile.minimal

@@ -22,6 +22,7 @@ SOURCES = \
 	src/filesystem/dummy/*.c \
 	src/render/*.c \
 	src/render/software/*.c \
+	src/sensor/*.c \
 	src/stdlib/*.c \
 	src/thread/*.c \
 	src/thread/generic/*.c \

+ 1 - 1
Makefile.pandora

@@ -13,7 +13,7 @@ CFLAGS  = -O3 -march=armv7-a -mcpu=cortex-a8 -mtune=cortex-a8 -mfloat-abi=softfp
 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/file/*.c ./src/sensor/*.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 \

+ 1 - 0
Makefile.psp

@@ -42,6 +42,7 @@ OBJS= src/SDL.o \
       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/stdlib/SDL_getenv.o \
       src/stdlib/SDL_iconv.o \
       src/stdlib/SDL_malloc.o \

+ 1 - 1
Makefile.wiz

@@ -13,7 +13,7 @@ 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/file/*.c ./src/sensor/*.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 \

+ 41 - 0
Xcode/SDL/SDL.xcodeproj/project.pbxproj

@@ -904,6 +904,18 @@
 		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 */; };
+		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 */; };
@@ -1229,6 +1241,10 @@
 		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>"; };
+		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; };
@@ -1348,6 +1364,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 */,
@@ -1856,6 +1873,7 @@
 				04BDFE2F12E6671700899322 /* loadso */,
 				04BDFE4512E6671700899322 /* power */,
 				041B2C9712FA0D680087D585 /* render */,
+				F3950CDB212BC8BC00F51292 /* sensor */,
 				04BDFE5D12E6671700899322 /* stdlib */,
 				04BDFE6412E6671800899322 /* thread */,
 				04BDFE9512E6671800899322 /* timer */,
@@ -1980,6 +1998,17 @@
 			name = "Linked Frameworks";
 			sourceTree = "<group>";
 		};
+		F3950CDB212BC8BC00F51292 /* sensor */ = {
+			isa = PBXGroup;
+			children = (
+				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;
 			children = (
@@ -2011,6 +2040,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 */,
@@ -2031,7 +2061,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 */,
@@ -2186,6 +2218,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 */,
@@ -2230,6 +2263,7 @@
 				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 */,
@@ -2269,6 +2303,7 @@
 				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 */,
@@ -2352,6 +2387,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 */,
@@ -2396,6 +2432,7 @@
 				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 */,
@@ -2435,6 +2472,7 @@
 				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 */,
@@ -2774,6 +2812,7 @@
 				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 */,
@@ -2909,6 +2948,7 @@
 				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 */,
@@ -3044,6 +3084,7 @@
 				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 */,

+ 17 - 0
configure

@@ -785,6 +785,7 @@ enable_render
 enable_events
 enable_joystick
 enable_haptic
+enable_sensor
 enable_power
 enable_filesystem
 enable_threads
@@ -1523,6 +1524,7 @@ Optional Features:
   --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]]
@@ -16884,6 +16886,7 @@ SOURCES="$SOURCES $srcdir/src/power/*.c"
 #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"
@@ -16989,6 +16992,20 @@ $as_echo "#define SDL_HAPTIC_DISABLED 1" >>confdefs.h
 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 :
   enableval=$enable_power;

+ 9 - 0
configure.in

@@ -346,6 +346,7 @@ SOURCES="$SOURCES $srcdir/src/power/*.c"
 #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"
@@ -410,6 +411,14 @@ if test x$enable_haptic != xyes; then
 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]]]),
               , enable_power=yes)

+ 3 - 1
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 @@ extern "C" {
 #define SDL_INIT_HAPTIC         0x00001000u
 #define SDL_INIT_GAMECONTROLLER 0x00002000u  /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
 #define SDL_INIT_EVENTS         0x00004000u
+#define SDL_INIT_SENSOR         0x00008000u
 #define SDL_INIT_NOPARACHUTE    0x00100000u  /**< compatibility; this flag is ignored. */
 #define SDL_INIT_EVERYTHING ( \
                 SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \
-                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \
+                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR \
             )
 /* @} */
 

+ 3 - 0
include/SDL_config_android.h

@@ -137,6 +137,9 @@
 #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
 

+ 15 - 0
include/SDL_events.h

@@ -144,6 +144,9 @@ typedef enum
     SDL_AUDIODEVICEADDED = 0x1100, /**< A new audio device is available */
     SDL_AUDIODEVICEREMOVED,        /**< An audio device has been removed. */
 
+    /* Sensor events */
+    SDL_SENSORUPDATE = 0x1200,     /**< A sensor was updated */
+
     /* Render events */
     SDL_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
     SDL_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */
@@ -471,6 +474,17 @@ typedef struct SDL_DropEvent
 } SDL_DropEvent;
 
 
+/**
+ *  \brief Sensor event structure (event.sensor.*)
+ */
+typedef struct SDL_SensorEvent
+{
+    Uint32 type;        /**< ::SDL_SENSORUPDATE */
+    Uint32 timestamp;   /**< In milliseconds, populated using SDL_GetTicks() */
+    Sint32 which;       /**< The instance ID of the sensor */
+    float data[6];      /**< Up to 6 values from the sensor - additional values can be queried using SDL_SensorGetData() */
+} SDL_SensorEvent;
+
 /**
  *  \brief The "quit requested" event
  */
@@ -542,6 +556,7 @@ typedef union SDL_Event
     SDL_ControllerButtonEvent cbutton;  /**< Game Controller button event data */
     SDL_ControllerDeviceEvent cdevice;  /**< Game Controller device event data */
     SDL_AudioDeviceEvent adevice;   /**< Audio device event data */
+    SDL_SensorEvent sensor;         /**< Sensor event data */
     SDL_QuitEvent quit;             /**< Quit request event data */
     SDL_UserEvent user;             /**< Custom event data */
     SDL_SysWMEvent syswm;           /**< System dependent window event data */

+ 201 - 0
include/SDL_sensor.h

@@ -0,0 +1,201 @@
+/*
+  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
+
+/**
+ *  \file 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_JoystickID;
+typedef int SDL_SensorID;
+
+/* The different sensor types */
+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;
+
+/* 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: */

+ 24 - 0
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
@@ -232,6 +233,20 @@ SDL_InitSubSystem(Uint32 flags)
 #endif
     }
 
+    /* Initialize the sensor subsystem */
+    if ((flags & SDL_INIT_SENSOR)){
+#if !SDL_SENSOR_DISABLED
+        if (SDL_PrivateShouldInitSubsystem(SDL_INIT_SENSOR)) {
+            if (SDL_SensorInit() < 0) {
+                return (-1);
+            }
+        }
+        SDL_PrivateSubsystemRefCountIncr(SDL_INIT_SENSOR);
+#else
+        return SDL_SetError("SDL not built with sensor support");
+#endif
+    }
+
     return (0);
 }
 
@@ -245,6 +260,15 @@ void
 SDL_QuitSubSystem(Uint32 flags)
 {
     /* Shut down requested initialized subsystems */
+#if !SDL_SENSOR_DISABLED
+    if ((flags & SDL_INIT_SENSOR)) {
+        if (SDL_PrivateShouldQuitSubsystem(SDL_INIT_SENSOR)) {
+			SDL_SensorQuit();
+		}
+        SDL_PrivateSubsystemRefCountDecr(SDL_INIT_SENSOR);
+    }
+#endif
+
 #if !SDL_JOYSTICK_DISABLED
     if ((flags & SDL_INIT_GAMECONTROLLER)) {
         /* game controller implies joystick */

+ 15 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -681,3 +681,18 @@
 #define SDL_GameControllerRumble SDL_GameControllerRumble_REAL
 #define SDL_JoystickRumble SDL_JoystickRumble_REAL
 #define SDL_IsTablet SDL_IsTablet_REAL
+#define SDL_IsTablet SDL_IsTablet_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

+ 14 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -725,3 +725,17 @@ SDL_DYNAPI_PROC(int,SDL_JoystickRumble,(SDL_Joystick *a, Uint16 b, Uint16 c, Uin
 #if defined(__ANDROID__) || defined(__IPHONEOS__)
 SDL_DYNAPI_PROC(SDL_bool,SDL_IsTablet,(void),(),return)
 #endif
+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),(),)

+ 7 - 0
src/events/SDL_events.c

@@ -657,6 +657,13 @@ SDL_PumpEvents(void)
     }
 #endif
 
+#if !SDL_SENSOR_DISABLED
+    /* Check for sensor state change */
+    if (!SDL_disabled_events[SDL_SENSORUPDATE >> 8]) {
+        SDL_SensorUpdate();
+    }
+#endif
+
     SDL_SendPendingQuit();  /* in case we had a signal handler fire, etc. */
 }
 

+ 548 - 0
src/sensor/SDL_sensor.c

@@ -0,0 +1,548 @@
+/*
+  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[] = {
+#if 0 //defined(__IPHONEOS__) || defined(__TVOS__)
+    &SDL_IOS_SensorDriver,
+#endif
+#ifdef SDL_SENSOR_ANDROID
+    &SDL_ANDROID_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;
+
+void
+SDL_LockSensors(void)
+{
+    if (SDL_sensor_lock) {
+        SDL_LockMutex(SDL_sensor_lock);
+    }
+}
+
+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 */
+
+    if (SDL_arraysize(SDL_sensor_drivers) == 0) {
+        /* What should we return here? We'll just return 0 with no sensors for now. */
+        status = 0;
+    }
+
+    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
+ */
+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: */

+ 38 - 0
src/sensor/SDL_sensor_c.h

@@ -0,0 +1,38 @@
+/*
+  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"
+
+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);
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 97 - 0
src/sensor/SDL_syssensor.h

@@ -0,0 +1,97 @@
+/*
+  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"
+
+/* 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;
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 211 - 0
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: */

+ 31 - 0
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: */

+ 4 - 0
test/Makefile.in

@@ -55,6 +55,7 @@ TARGETS = \
 	testrumble$(EXE) \
 	testscale$(EXE) \
 	testsem$(EXE) \
+	testsensor$(EXE) \
 	testshader$(EXE) \
 	testshape$(EXE) \
 	testsprite2$(EXE) \
@@ -240,6 +241,9 @@ testscale$(EXE): $(srcdir)/testscale.c
 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@
 

+ 117 - 0
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);
+}