1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-05-12 12:00:13 +00:00

Reorg. game controller and update libstem_gamepad

This commit is contained in:
Samuel Gomes 2023-09-26 02:01:35 +05:30
parent 059a41a86f
commit 79c531f2bb
33 changed files with 469 additions and 4954 deletions

View file

@ -270,8 +270,17 @@ endif
ifneq ($(filter y,$(DEP_DEVICEINPUT)),)
EXE_LIBS += $(QB_DEVICE_INPUT_LIB)
CXXFLAGS += -DDEPENDENCY_DEVICEINPUT
ifeq ($(OS),win)
CXXLIBS += -lwinmm -lxinput -ldinput8 -ldxguid -lwbemuuid -lole32 -loleaut32
endif
ifeq ($(OS),osx)
CXXLIBS += -framework CoreFoundation -framework IOKit
endif
QBLIB_NAME := $(addsuffix 1,$(QBLIB_NAME))
LICENSE_IN_USE += libstem_gamepad
else
QBLIB_NAME := $(addsuffix 0,$(QBLIB_NAME))
endif
@ -345,10 +354,6 @@ ifeq ($(OS),win)
CXXLIBS += -lwinspool
endif
ifneq ($(filter y,$(DEP_DEVICEINPUT)),)
CXXLIBS += -lwinmm
endif
ifneq ($(filter y,$(DEP_ICON) $(DEP_ICON_RC) $(DEP_SCREENIMAGE) $(DEP_PRINTER)),)
CXXLIBS += -lgdi32
endif

View file

@ -22,6 +22,7 @@
#include "datetime.h"
#include "event.h"
#include "font.h"
#include "game_controller.h"
#include "glut-thread.h"
#include "gui.h"
#include "http.h"
@ -1236,31 +1237,9 @@ int32 key_display_state = 0;
int32 key_display = 0;
int32 key_display_redraw = 0;
extern int32 device_last;
extern int32 device_max;
extern device_struct *devices;
extern uint8 getDeviceEventButtonValue(device_struct *device, int32 eventIndex, int32 objectIndex);
extern void setDeviceEventButtonValue(device_struct *device, int32 eventIndex, int32 objectIndex, uint8 value);
extern float getDeviceEventAxisValue(device_struct *device, int32 eventIndex, int32 objectIndex);
extern void setDeviceEventAxisValue(device_struct *device, int32 eventIndex, int32 objectIndex, float value);
extern float getDeviceEventWheelValue(device_struct *device, int32 eventIndex, int32 objectIndex);
extern void setDeviceEventWheelValue(device_struct *device, int32 eventIndex, int32 objectIndex, float value);
extern void setupDevice(device_struct *device);
extern int32 createDeviceEvent(device_struct *device);
extern void commitDeviceEvent(device_struct *device);
extern ontimer_struct *ontimer;
extern onkey_struct *onkey;
extern int32 onkey_inprogress;
extern onstrig_struct *onstrig;
extern int32 onstrig_inprogress;
extern uint32 qbevent;
#ifdef DEPENDENCY_DEVICEINPUT
# include "parts/input/game_controller/src.c"
#endif
extern int32 console;
extern int32 screen_hide_startup;

View file

@ -7,5 +7,6 @@ void error(int32_t error_number);
void evnt(uint32_t linenumber, uint32_t inclinenumber = 0, const char *incfilename = NULL);
extern uint32_t new_error;
extern uint32_t qbevent;
#endif

View file

@ -0,0 +1,29 @@
//----------------------------------------------------------------------------------------------------------------------
// QB64-PE Game Controller Library
// Powered by libstem Gamepad (https://github.com/ThemsAllTook/libstem_gamepad)
//----------------------------------------------------------------------------------------------------------------------
#include <stdint.h>
struct device_struct;
struct onstrig_struct;
extern int32_t device_last;
extern int32_t device_max;
extern device_struct *devices;
extern onstrig_struct *onstrig;
extern int32_t onstrig_inprogress;
uint8_t getDeviceEventButtonValue(device_struct *device, int32_t eventIndex, int32_t objectIndex);
void setDeviceEventButtonValue(device_struct *device, int32_t eventIndex, int32_t objectIndex, uint8_t value);
float getDeviceEventAxisValue(device_struct *device, int32_t eventIndex, int32_t objectIndex);
void setDeviceEventAxisValue(device_struct *device, int32_t eventIndex, int32_t objectIndex, float value);
float getDeviceEventWheelValue(device_struct *device, int32_t eventIndex, int32_t objectIndex);
void setDeviceEventWheelValue(device_struct *device, int32_t eventIndex, int32_t objectIndex, float value);
void setupDevice(device_struct *device);
int32_t createDeviceEvent(device_struct *device);
void commitDeviceEvent(device_struct *device);
void QB64_GAMEPAD_INIT();
void QB64_GAMEPAD_POLL();
void QB64_GAMEPAD_SHUTDOWN();

View file

@ -4,21 +4,27 @@ ifeq ($(OS),lnx)
endif
ifeq ($(OS),win)
GAMEPAD_SRCS := Gamepad_windows_mm.c Gamepad_private.c
GAMEPAD_SRCS := Gamepad_windows_dinput.c Gamepad_private.c
endif
ifeq ($(OS),osx)
GAMEPAD_SRCS := Gamepad_macosx.c Gamepad_private.c
endif
GAMEPAD_OBJS := $(GAMEPAD_SRCS:.c=.o)
GAMEPAD_OBJS := $(patsubst %,$(PATH_INTERNAL_C)/parts/input/game_controller/src/%,$(GAMEPAD_OBJS))
GAMECONTROLLER_SRCS := game_controller.cpp
$(PATH_INTERNAL_C)/parts/input/game_controller/src/%.o: $(PATH_INTERNAL_C)/parts/input/game_controller/src/%.c
$(CC) -Wall $< -c -o $@
GAMEPAD_OBJS := $(patsubst %.c,$(PATH_INTERNAL_C)/parts/input/game_controller/libstem_gamepad/%.o,$(GAMEPAD_SRCS))
QB_DEVICE_INPUT_LIB := $(PATH_INTERNAL_C)/parts/input/game_controller/src.a
GAMECONTROLLER_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/input/game_controller/%.o,$(GAMECONTROLLER_SRCS))
$(QB_DEVICE_INPUT_LIB): $(GAMEPAD_OBJS)
$(AR) rcs $@ $(GAMEPAD_OBJS)
$(PATH_INTERNAL_C)/parts/input/game_controller/libstem_gamepad/%.o: $(PATH_INTERNAL_C)/parts/input/game_controller/libstem_gamepad/%.c
$(CC) -O2 $(CFLAGS) -Wall $< -c -o $@
$(PATH_INTERNAL_C)/parts/input/game_controller/%.o: $(PATH_INTERNAL_C)/parts/input/game_controller/%.cpp
$(CXX) -O2 $(CXXFLAGS) -Wall $< -c -o $@
QB_DEVICE_INPUT_LIB := $(PATH_INTERNAL_C)/parts/input/game_controller/game_controller.a
$(QB_DEVICE_INPUT_LIB): $(GAMEPAD_OBJS) $(GAMECONTROLLER_OBJS)
$(AR) rcs $@ $(GAMEPAD_OBJS) $(GAMECONTROLLER_OBJS)

View file

@ -1,47 +0,0 @@
1.4.0 (2014-04-06)
* Windows implementation completely redone to use DirectInput and XInput instead of joyGetPosEx
* Added MSVC support
1.3.1 (2014-02-01)
* Gamepad_init() on Mac OS X now detects devices immediately rather than waiting until the next run loop cycle
* Gamepad_detectDevices() and Gamepad_processEvents() now work on Mac OS X in applicactions that call them outside the main CF/NSRunLoop
* Fixed a bug on Mac OS X that could cause a crash on Gamepad_shutdown()
* Removed all remaining stem library dependencies (shell, glutshell, and glgraphics for testharness)
1.3.0 (2013-09-01)
* Gamepad callbacks now include context pointers
* Axis move callback now reports previous axis value in addition to current one
* Fixed a major bug (introduced in 1.2.0) that caused button up events never to be reported on Windows
1.2.0 (2013-07-18)
* Removed dependencies on utilities and stemobject. Library is now completely standalone, though test harness still requires shell and glutshell.
* Gamepad API no longer uses EventDispatcher, instead providing GLUT-style callback registration for all previous event types
1.1.6 (2013-07-17)
* Added 64-bit Windows and Linux support
1.1.5 (2011-11-16)
* Gamepad_detectDevices() significantly sped up on Linux
1.1.4 (2011-11-08)
* Gamepad_processEvents() will now do nothing if called from within itself
1.1.3 (2011-09-29)
* Updated event dispatching to work with utilities 1.5.0
1.1.2 (2011-09-20)
* Updated dependencies: utilities 1.4.0 -> 1.4.2
1.1.1 (2011-08-24)
* Fixed crashes from retrieving gamepad description strings
* Fixed a memory leak
* Fixed a potential thread deadlock on Linux
* Removed leftover joystick debug code
1.1.0 (2010-01-28)
* Added vendor and product ID fields to Gamepad_device
* Worked around a crash on the Mac with Sixaxis controllers
* Fixed a problem that caused Saitek X52 hat switches to report incorrect values on Mac OS X
1.0.0 (2010-01-19)
* Initial version

View file

@ -1,787 +0,0 @@
.PHONY: all
all: library testharness include
UNAME = ${shell uname}
ifeq (${UNAME},Linux)
-include ~/.stem.defines
STEM_SHARED_DIR ?= /usr/local/stem
HOST_PLATFORM = linux
else ifeq (${UNAME},Darwin)
-include ~/.stem.defines
STEM_SHARED_DIR ?= /usr/local/stem
HOST_PLATFORM = macosx
else
STEM_SHARED_DIR ?= C:/stem
-include ${STEM_SHARED_DIR}/stem.defines
HOST_PLATFORM = windows
endif
include version
define newline_and_tab
endef
iphone_sdk_version_integer = ${subst .,0,$1}${word ${words ${wordlist 2, ${words ${subst ., ,$1}}, ${subst ., ,$1}}}, 00}
TARGET_PLATFORMS_macosx = macosx iphonesimulator iphoneos
TARGET_PLATFORMS_linux = linux32 linux64
TARGET_PLATFORMS_windows = win32 win64
PROJECT_NAME = gamepad
IPHONE_BUILD_SDK_VERSION ?= 4.2
IPHONE_DEPLOYMENT_TARGET_VERSION ?= 3.1
CODESIGN_IDENTITY ?= "iPhone Developer"
LIBRARY_TARGETS = library
EXECUTABLE_TARGETS =
APPLICATION_TARGETS = testharness
TARGETS = ${LIBRARY_TARGETS} ${EXECUTABLE_TARGETS} ${APPLICATION_TARGETS}
PLATFORMS = ${filter ${TARGET_PLATFORMS_${HOST_PLATFORM}},macosx linux32 linux64 win32 win64}
ANALYZERS = splint clang
TARGET_NAME_library = libstem_${PROJECT_NAME}
TARGET_NAME_unittest = ${PROJECT_NAME}_unittest
TARGET_NAME_testharness = ${PROJECT_NAME}_testharness
HUMAN_READABLE_TARGET_NAME_testharness = GamepadTestHarness
#Per-target configurations
CONFIGURATIONS_library = debug profile release
CONFIGURATIONS_unittest = debug
CONFIGURATIONS_testharness = debug profile release
#Per-target platforms
PLATFORMS_library = ${filter ${PLATFORMS},macosx linux32 linux64 win32 win64}
PLATFORMS_unittest = ${filter ${PLATFORMS},macosx linux32 linux64 win32 win64}
PLATFORMS_testharness = ${filter ${PLATFORMS},macosx linux32 linux64 win32 win64}
#Per-target compile/link settings
CCFLAGS_testharness = -DGLEW_STATIC
#Per-target analyzer settings
CLANGFLAGS_unittest = ${CCFLAGS_unittest}
SPLINTFLAGS_unittest = ${CCFLAGS_unittest}
#Per-configuration compile/link settings
CCFLAGS_debug = -g -DDEBUG
CCFLAGS_profile = -g -O3
CCFLAGS_release = -O3
#Per-platform compile/link settings
CC_macosx_i386 = /usr/bin/clang -arch i386
CC_macosx_x86_64 = /usr/bin/clang -arch x86_64
AR_macosx = /usr/bin/ar
RANLIB_macosx = /usr/bin/ranlib
SPLINT_macosx = /usr/local/bin/splint
CLANG_macosx = /usr/bin/clang
SDKROOT_macosx = /Developer/SDKs/MacOSX10.6.sdk
ARCHS_macosx = i386 x86_64
CCFLAGS_macosx = -isysroot ${SDKROOT_macosx} -mmacosx-version-min=10.6
LINKFLAGS_macosx = -isysroot ${SDKROOT_macosx} -mmacosx-version-min=10.6 -framework IOKit -framework CoreFoundation -framework OpenGL -framework GLUT -framework ApplicationServices
CC_linux32_i386 = /usr/bin/gcc
AR_linux32 = /usr/bin/ar
RANLIB_linux32 = /usr/bin/ranlib
SPLINT_linux32 = /usr/local/bin/splint
CLANG_linux32 = /usr/local/bin/clang
ARCHS_linux32 = i386
CCFLAGS_linux32 = -m32
LINKFLAGS_linux32 = -m32 -ldl -lglut -lGLU -lGL -lm -Wl,-E
CC_linux64_x86_64 = /usr/bin/gcc
AR_linux64 = /usr/bin/ar
RANLIB_linux64 = /usr/bin/ranlib
SPLINT_linux64 = /usr/local/bin/splint
CLANG_linux64 = /usr/local/bin/clang
ARCHS_linux64 = x86_64
CCFLAGS_linux64 = -m64
LINKFLAGS_linux64 = -m64 -ldl -lglut -lGLU -lGL -lm -Wl,-E
MINGW_W32_PATH ?= C:/MinGW
MINGW_W32_VERSION ?= 4.6.2
SPLINT_WIN_PATH ?= C:/splint-3.1.1/bin/splint.exe
CLANG_WIN_PATH ?= C:/llvm/bin/clang.exe
DX9_INCLUDE_PATH ?= C:/MinGW/dx9/include
DX9_LIB_PATH ?= C:/MinGW/dx9/lib
DX9_LIB_PATH_i386 ?= ${DX9_LIB_PATH}/x86
WMI_LIB_PATH_i386 ?= C:/MinGW/WinSDK/Lib
CC_win32_i386 = ${MINGW_W32_PATH}/bin/gcc.exe
AR_win32 = ${MINGW_W32_PATH}/bin/ar.exe
RANLIB_win32 = ${MINGW_W32_PATH}/bin/ranlib.exe
SPLINT_win32 = ${SPLINT_WIN_PATH}
CLANG_win32 = ${CLANG_WIN_PATH}
ARCHS_win32 = i386
CCFLAGS_win32 = -DFREEGLUT_STATIC -I ${DX9_INCLUDE_PATH}
LINKFLAGS_win32 = -lfreeglut32_static -lopengl32 -lglu32 -lpthread -lwinmm -lgdi32 ${DX9_LIB_PATH_i386}/Xinput.lib ${DX9_LIB_PATH_i386}/dinput8.lib ${DX9_LIB_PATH_i386}/dxguid.lib ${WMI_LIB_PATH_i386}/WbemUuid.Lib ${WMI_LIB_PATH_i386}/Ole32.Lib ${WMI_LIB_PATH_i386}/OleAut32.Lib
EXECUTABLE_SUFFIX_win32 = .exe
MINGW_W64_PATH ?= C:/MinGW-w64
MINGW_W64_VERSION ?= 4.7.0
DX9_LIB_PATH_x86_64 ?= ${DX9_LIB_PATH}/x64
WMI_LIB_PATH_x86_64 ?= C:/MinGW/WinSDK/Lib/x64
CC_win64_x86_64 = ${MINGW_W64_PATH}/bin/x86_64-w64-mingw32-gcc.exe
AR_win64 = ${MINGW_W64_PATH}/bin/x86_64-w64-mingw32-ar.exe
RANLIB_win64 = ${MINGW_W64_PATH}/bin/x86_64-w64-mingw32-ranlib.exe
SPLINT_win64 = ${SPLINT_WIN_PATH}
CLANG_win64 = ${CLANG_WIN_PATH}
ARCHS_win64 = x86_64
CCFLAGS_win64 = -DFREEGLUT_STATIC -I ${DX9_INCLUDE_PATH}
LINKFLAGS_win64 = -lfreeglut64_static -lopengl32 -lglu32 -lpthread -lwinmm -lgdi32 ${DX9_LIB_PATH_x86_64}/Xinput.lib ${DX9_LIB_PATH_x86_64}/dinput8.lib ${DX9_LIB_PATH_x86_64}/dxguid.lib ${WMI_LIB_PATH_x86_64}/WbemUuid.Lib ${WMI_LIB_PATH_x86_64}/Ole32.Lib ${WMI_LIB_PATH_x86_64}/OleAut32.Lib
EXECUTABLE_SUFFIX_win64 = .exe
#General compile/link settings
DEFINE_CCFLAGS = -DVERSION_MAJOR=${VERSION_MAJOR}u -DVERSION_MINOR=${VERSION_MINOR}u -DVERSION_TWEAK=${VERSION_TWEAK}u
WARNING_CCFLAGS = -Wall -Wextra -Wno-unused-parameter -Werror
OTHER_CCFLAGS = -std=gnu99
CCFLAGS = ${DEFINE_CCFLAGS} ${WARNING_CCFLAGS} ${OTHER_CCFLAGS}
FRAMEWORK_LINKFLAGS =
LIBRARY_LINKFLAGS =
OTHER_LINKFLAGS =
LINKFLAGS = ${FRAMEWORK_LINKFLAGS} ${LIBRARY_LINKFLAGS} ${OTHER_LINKFLAGS}
LINK_ORDER = \
library
#Dependencies (can optionally be per-target or per-target-per-platform)
PROJECT_LIBRARY_DEPENDENCIES_unittest = library
PROJECT_LIBRARY_DEPENDENCIES_testharness = library
STEM_LIBRARY_DEPENDENCIES =
STEM_LIBRARY_DEPENDENCIES_testharness =
STEM_SOURCE_DEPENDENCIES =
THIRDPARTY_LIBRARY_DEPENDENCIES =
#Per-target source file lists
SOURCES_library = \
source/${PROJECT_NAME}/Gamepad_private.c
SOURCES_library_macosx = \
source/${PROJECT_NAME}/Gamepad_macosx.c
SOURCES_library_win32 = \
source/${PROJECT_NAME}/Gamepad_windows_dinput.c
SOURCES_library_win64 = \
source/${PROJECT_NAME}/Gamepad_windows_dinput.c
SOURCES_library_linux32 = \
source/${PROJECT_NAME}/Gamepad_linux.c
SOURCES_library_linux64 = \
source/${PROJECT_NAME}/Gamepad_linux.c
SOURCES_unittest = \
build/intermediate/TestList.c \
${SOURCES_unittest_suites}
SOURCES_unittest_suites =
SOURCES_testharness = \
source/testharness/TestHarness_main.c
#Include files to be distributed with library
INCLUDES = \
source/${PROJECT_NAME}/Gamepad.h
#Target resources
RESOURCES_testharness =
RESOURCES_unittest =
RESOURCES_testharness_macosx =
#...
#General analyzer settings
CLANGFLAGS =
CLANGFLAGS_win32 = -I ${MINGW_W32_PATH}/include -I ${MINGW_W32_PATH}/lib/gcc/mingw32/${MINGW_W32_VERSION}/include
CLANGFLAGS_win64 = -I ${MINGW_W64_PATH}/include -I ${MINGW_W64_PATH}/lib/gcc/mingw32/${MINGW_W64_VERSION}/include
SPLINTFLAGS = -exportlocal
#Source files excluded from static analysis
ANALYZER_EXCLUDE_SOURCES_clang =
ANALYZER_EXCLUDE_SOURCES_splint = ${SOURCES_unittest}
#Additional target build prerequisites
PREREQS_unittest =
#TestList.c is automatically generated from ${SOURCES_unittest_suites}. It is used by the unit test framework to determine which tests to run.
build/intermediate/TestList.c: build/intermediate/TestSuites.txt | build/intermediate
echo 'const char * UnitTest_suiteNameList[] = {${foreach file,${SOURCES_unittest_suites},"${basename ${notdir ${file}}}",} (void *) 0};' > $@
#TestSuites.txt tracks the state of ${SOURCES_unittest_suites} so that TestList.c can be updated if and only if ${SOURCES_unittest_suites} has changed. .PHONY is abused slightly to cause the target to be conditionally remade.
ifneq (${shell echo "${SOURCES_unittest_suites}" | cmp - build/intermediate/TestSuites.txt 2>&1},)
.PHONY: build/intermediate/TestSuites.txt
endif
build/intermediate/TestSuites.txt: | build/intermediate
echo "${SOURCES_unittest_suites}" > $@
define configuration_object_list_template #(target, configuration)
${foreach platform,${PLATFORMS_$1}, \
${call platform_object_list_template,$1,$2,${platform}} \
}
endef
define platform_object_list_template #(target, configuration, platform)
${foreach arch,${ARCHS_$3}, \
${call arch_object_list_template,$1,$2,$3,${arch}} \
}
endef
define arch_object_list_template #(target, configuration, platform, arch)
${foreach source,${SOURCES_$1} ${SOURCES_$1_$3}, \
build/intermediate/$1-$2-$3-$4/${notdir ${basename ${source}}}.o \
}
endef
#Produces OBJECTS_${target}_${configuration} variables for each permutation of target and configuration in that target
${foreach target,${TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${eval OBJECTS_${target}_${configuration} = ${call configuration_object_list_template,${target},${configuration}}} \
} \
}
define create_directory_target_template #(dir)
.LOW_RESOLUTION_TIME: $1
$1:
mkdir -p $1
endef
${foreach target,${TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval ${call create_directory_target_template,build/${target}/${configuration}-${platform}}} \
${foreach arch,${ARCHS_${platform}}, \
${eval ${call create_directory_target_template,build/intermediate/${target}-${configuration}-${platform}-${arch}}} \
} \
} \
} \
}
${eval ${call create_directory_target_template,build/intermediate}}
define include_ccflags_template #(target, platform)
-I source \
${foreach stem_dependency,${STEM_LIBRARY_DEPENDENCIES} ${STEM_LIBRARY_DEPENDENCIES_$1} ${STEM_LIBRARY_DEPENDENCIES_$2} ${STEM_LIBRARY_DEPENDENCIES_$1_$2},-I ${STEM_SHARED_DIR}/${stem_dependency}/include} \
${foreach thirdparty_dependency,${THIRDPARTY_LIBRARY_DEPENDENCIES} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$2} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1_$2},-I ${STEM_SHARED_DIR}/${dir ${thirdparty_dependency}}include} \
${foreach source_dependency,${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$2} ${STEM_SOURCE_DEPENDENCIES_$1_$2},-I dep/${word 1,${subst /, ,${source_dependency}}}/source}
endef
define define_ccflags_template #(target, configuration, platform, arch)
-DSTEM_TARGET_$1 -DSTEM_CONFIGURATION_$2 -DSTEM_PLATFORM_$3 -DSTEM_ARCH_$4
endef
define dependency_template #(target, configuration, platform, arch, source_file)
build/intermediate/$1-$2-$3-$4/${notdir ${basename $5}}.d: $5 ${PREREQS_$1} | build/intermediate/$1-$2-$3-$4
@${CC_$3_$4} ${CCFLAGS} ${CCFLAGS_$1} ${CCFLAGS_$2} ${CCFLAGS_$3} ${call include_ccflags_template,$1,$3} ${call define_ccflags_template,$1,$2,$3,$4} -MM -o $$@.temp $5
@sed 's,\(${notdir ${basename $5}}\)\.o[ :]*,$${basename $$@}.o $${basename $$@}.d: ,g' < $$@.temp > $$@
@rm $$@.temp
endef
#Produces dependency build targets for all source files in each configuration/platform/arch
ifeq ($(filter clean full_dist commit_dist analyze analyze_clang analyze_splint,${MAKECMDGOALS}),)
${foreach target,${TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${foreach arch,${ARCHS_${platform}}, \
${foreach source,${SOURCES_${target}} ${SOURCES_${target}_${platform}}, \
${eval ${call dependency_template,${target},${configuration},${platform},${arch},${source}}} \
${eval -include build/intermediate/${target}-${configuration}-${platform}-${arch}/${notdir ${basename ${source}}}.d} \
} \
} \
} \
} \
}
endif
define compile_template #(target, configuration, platform, arch, source_file)
build/intermediate/$1-$2-$3-$4/${notdir ${basename $5}}.o: $5 ${PREREQS_$1} | build/intermediate/$1-$2-$3-$4
${CC_$3_$4} ${CCFLAGS} ${CCFLAGS_$1} ${CCFLAGS_$2} ${CCFLAGS_$3} ${call include_ccflags_template,$1,$3} ${call define_ccflags_template,$1,$2,$3,$4} -c -o $$@ $5
endef
#Produces object build targets for all source files in each configuration/platform/arch
${foreach target,${TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${foreach arch,${ARCHS_${platform}}, \
${foreach source,${SOURCES_${target}} ${SOURCES_${target}_${platform}}, \
${eval ${call compile_template,${target},${configuration},${platform},${arch},${source}}} \
} \
} \
} \
} \
}
define library_template #(target, configuration, platform, arch, output_file)
build/intermediate/$1-$2-$3-$4/$5: ${call arch_object_list_template,$1,$2,$3,$4}
${AR_$3} rc $$@ $$^
${RANLIB_$3} $$@
endef
#Produces static library build targets for each arch/platform/target for library targets
${foreach target,${LIBRARY_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${foreach arch,${ARCHS_${platform}}, \
${eval ${call library_template,${target},${configuration},${platform},${arch},${TARGET_NAME_${target}}.a}} \
} \
} \
} \
}
define executable_template #(target, configuration, platform, arch, output_file, dependent_libraries)
build/intermediate/$1-$2-$3-$4/$5: ${call arch_object_list_template,$1,$2,$3,$4} $6
${CC_$3_$4} -o $$@ $$^ ${LINKFLAGS} ${LINKFLAGS_$3}
endef
define library_dependency_template #(target, configuration, platform)
${foreach link_library,${LINK_ORDER}, \
${foreach library,${filter ${link_library}%,${PROJECT_LIBRARY_DEPENDENCIES} ${PROJECT_LIBRARY_DEPENDENCIES_$1} ${PROJECT_LIBRARY_DEPENDENCIES_$3} ${PROJECT_LIBRARY_DEPENDENCIES_$1_$3}}, \
build/${library}/$2-$3/${TARGET_NAME_${library}}.a \
} \
${foreach library,${filter ${link_library}%,${STEM_LIBRARY_DEPENDENCIES} ${STEM_LIBRARY_DEPENDENCIES_$1} ${STEM_LIBRARY_DEPENDENCIES_$3} ${STEM_LIBRARY_DEPENDENCIES_$1_$3}}, \
${STEM_SHARED_DIR}/${library}/library/$2-$3/libstem_${word 1,${subst /, ,${library}}}.a \
} \
${foreach library,${filter ${link_library}%,${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$3} ${STEM_SOURCE_DEPENDENCIES_$1_$3}}, \
dep/${word 1,${subst /, ,${library}}}/build/${word 2,${subst /, ,${library}}}/$2-$3/${word 3,${subst /, ,${library}}} \
} \
${foreach library,${filter ${link_library}%,${THIRDPARTY_LIBRARY_DEPENDENCIES} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$3} ${THIRDPARTY_LIBRARY_DEPENDENCIES_$1_$3}}, \
${STEM_SHARED_DIR}/${dir ${library}}library/$3/${notdir ${library}} \
} \
}
endef
#Produces executable build targets for each arch/platform/target for executable and application targets
${foreach target,${EXECUTABLE_TARGETS} ${APPLICATION_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${foreach arch,${ARCHS_${platform}}, \
${eval ${call executable_template,${target},${configuration},${platform},${arch},${TARGET_NAME_${target}}${EXECUTABLE_SUFFIX_${platform}},${call library_dependency_template,${target},${configuration},${platform}}}} \
} \
} \
} \
}
define dependency_submake_template #(dependency)
.PHONY: $1
$1:
${MAKE} -C dep/${word 1,${subst /, ,$1}}
endef
#Invokes make for each source dependency
${foreach dependency,${sort ${foreach target,${TARGETS},${foreach platform,${PLATFORMS_${target}},${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_${target}} ${STEM_SOURCE_DEPENDENCIES_${platform}} ${STEM_SOURCE_DEPENDENCIES_${target}_${platform}}}}}, \
${eval ${call dependency_submake_template,${dependency}}} \
}
define thin_binary_list_template #(target, configuration, platform, target_name)
${foreach arch,${ARCHS_$3}, \
build/intermediate/$1-$2-$3-${arch}/$4 \
}
endef
#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for library targets
${foreach target,${LIBRARY_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${target},${configuration},${platform},${TARGET_NAME_${target}}.a}} \
} \
} \
}
#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for executable targets
${foreach target,${EXECUTABLE_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${target},${configuration},${platform},${TARGET_NAME_${target}}${EXECUTABLE_SUFFIX_${platform}}}} \
} \
} \
}
#Produces THIN_BINARIES_${target}_${configuration}_${platform} variables for each target/configuration/platform for application targets
${foreach target,${APPLICATION_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval THIN_BINARIES_${target}_${configuration}_${platform} = ${call thin_binary_list_template,${target},${configuration},${platform},${TARGET_NAME_${target}}${EXECUTABLE_SUFFIX_${platform}}}} \
} \
} \
}
define assemble_library_macosx #(target, configuration, platform)
build/$1/$2-$3/${TARGET_NAME_$1}.a: ${THIN_BINARIES_$1_$2_$3} | build/$1/$2-$3
lipo -create -output $$@ ${THIN_BINARIES_$1_$2_$3}
endef
define assemble_library_linux #(target, configuration, platform)
build/$1/$2-$3/${TARGET_NAME_$1}.a: ${THIN_BINARIES_$1_$2_$3} | build/$1/$2-$3
cp ${THIN_BINARIES_$1_$2_$3} $$@
endef
define assemble_library_windows #(target, configuration, platform)
build/$1/$2-$3/${TARGET_NAME_$1}.a: ${THIN_BINARIES_$1_$2_$3} | build/$1/$2-$3
cp ${THIN_BINARIES_$1_$2_$3} $$@
endef
#Produces final library build targets
${foreach target,${LIBRARY_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval ${call assemble_library_${HOST_PLATFORM},${target},${configuration},${platform}}} \
} \
} \
}
define copy_target_resources #(target, platform, resources_dir)
${if ${strip ${RESOURCES_$1} ${RESOURCES_$1_$2}},mkdir -p $3,}
${foreach resource,${RESOURCES_$1} ${RESOURCES_$1_$2}, \
cp -r ${resource} $3${newline_and_tab} \
}
${if ${strip ${RESOURCES_$1} ${RESOURCES_$1_$2}},find $3 -name .svn -print0 -or -name .DS_Store -print0 | xargs -0 rm -rf}
endef
define assemble_executable_macosx #(target, configuration, platform)
build/$1/$2-$3/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_$3} ${RESOURCES_$1} ${RESOURCES_$1_$3} | build/$1/$2-$3
lipo -create -output $$@ ${THIN_BINARIES_$1_$2_$3}
${call copy_target_resources,$1,$3,$${dir $$@}}
endef
define assemble_executable_linux #(target, configuration, platform)
build/$1/$2-$3/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_$3} ${RESOURCES_$1} ${RESOURCES_$1_$3} | build/$1/$2-$3
cp ${THIN_BINARIES_$1_$2_$3} $$@
${call copy_target_resources,$1,$3,$${dir $$@}}
endef
define assemble_executable_windows #(target, configuration, platform)
build/$1/$2-$3/${TARGET_NAME_$1}.exe: ${THIN_BINARIES_$1_$2_$3} ${RESOURCES_$1} ${RESOURCES_$1_$3} | build/$1/$2-$3
cp ${THIN_BINARIES_$1_$2_$3} $$@
${call copy_target_resources,$1,$3,$${dir $$@}}
endef
#Produces final executable build targets
${foreach target,${EXECUTABLE_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval ${call assemble_executable_${HOST_PLATFORM},${target},${configuration},${platform}}} \
} \
} \
}
PLIST_FILE_testharness_macosx = resources/Info_testharness_macosx.plist
PLIST_FILE_testharness_iphonesimulator = resources/Info_testharness_iphone.plist
PLIST_PLATFORM_CASED_iphonesimulator = iPhoneSimulator
PLIST_PLATFORM_LOWER_iphonesimulator = iphonesimulator
PLIST_SDK_NAME_iphonesimulator = iphonesimulator${IPHONE_BUILD_SDK_VERSION}
PLIST_FILE_testharness_iphoneos = resources/Info_testharness_iphone.plist
PLIST_PLATFORM_CASED_iphoneos = iPhoneOS
PLIST_PLATFORM_LOWER_iphoneos = iphoneos
PLIST_SDK_NAME_iphoneos = iphoneos${IPHONE_BUILD_SDK_VERSION}
define create_app_bundle #(target, platform, executable_dir, plist_dir, resources_dir)
mkdir -p $3 $4 $5
sed -e "s/\$$$${PRODUCT_NAME}/${TARGET_NAME_$1}/g" \
-e "s/\$$$${HUMAN_READABLE_PRODUCT_NAME}/${HUMAN_READABLE_TARGET_NAME_$1}/g" \
-e "s/\$$$${VERSION}/${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_TWEAK}/g" \
-e "s/\$$$${COPYRIGHT_YEAR}/"`date +%Y`"/g" \
-e "s/\$$$${BUILD_NUMBER}/0/g" \
-e "s/\$$$${PLATFORM_CASED}/${PLIST_PLATFORM_CASED_$2}/g" \
-e "s/\$$$${PLATFORM_LOWER}/${PLIST_PLATFORM_LOWER_$2}/g" \
-e "s/\$$$${SDK}/${PLIST_SDK_NAME_$2}/g" \
${PLIST_FILE_$1_$2} > $4/Info.plist
echo "APPL????" > $4/PkgInfo
${call copy_target_resources,$1,$2,$5}
endef
define assemble_application_macosx #(target, configuration)
build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/MacOS/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_macosx} ${RESOURCES_$1} ${RESOURCES_$1_macosx} | build/$1/$2-macosx
${call create_app_bundle,$1,macosx,build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/MacOS,build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents,build/$1/$2-macosx/$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/Resources}
lipo -create -output "$$@" ${THIN_BINARIES_$1_$2_macosx}
endef
define assemble_application_iphonesimulator #(target, configuration)
build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_iphonesimulator} ${RESOURCES_$1} ${RESOURCES_$1_iphonesimulator} | build/$1/$2-iphonesimulator
${call create_app_bundle,$1,iphonesimulator,build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app,build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app,build/$1/$2-iphonesimulator/${TARGET_NAME_$1}.app}
lipo -create -output "$$@" ${THIN_BINARIES_$1_$2_iphonesimulator}
endef
define assemble_application_iphoneos #(target, configuration)
build/$1/$2-iphoneos/${TARGET_NAME_$1}.app/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_iphoneos} ${RESOURCES_$1} ${RESOURCES_$1_iphoneos} | build/$1/$2-iphoneos
${call create_app_bundle,$1,iphoneos,build/$1/$2-iphoneos/${TARGET_NAME_$1}.app,build/$1/$2-iphoneos/${TARGET_NAME_$1}.app,build/$1/$2-iphoneos/${TARGET_NAME_$1}.app}
lipo -create -output "$$@" ${THIN_BINARIES_$1_$2_iphoneos}
endef
define assemble_application_linux32 #(target, configuration)
build/$1/$2-linux32/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_linux32} ${RESOURCES_$1} ${RESOURCES_$1_linux32} | build/$1/$2-linux32
${call copy_target_resources,$1,linux32,build/$1/$2-linux32/Resources}
cp ${THIN_BINARIES_$1_$2_linux32} "$$@"
endef
define assemble_application_linux64 #(target, configuration)
build/$1/$2-linux64/${TARGET_NAME_$1}: ${THIN_BINARIES_$1_$2_linux64} ${RESOURCES_$1} ${RESOURCES_$1_linux64} | build/$1/$2-linux64
${call copy_target_resources,$1,linux64,build/$1/$2-linux64/Resources}
cp ${THIN_BINARIES_$1_$2_linux64} "$$@"
endef
define assemble_application_win32 #(target, configuration)
build/$1/$2-win32/${TARGET_NAME_$1}.exe: ${THIN_BINARIES_$1_$2_win32} ${RESOURCES_$1} ${RESOURCES_$1_win32} | build/$1/$2-win32
${call copy_target_resources,$1,win32,build/$1/$2-win32/Resources}
cp ${THIN_BINARIES_$1_$2_win32} "$$@"
endef
define assemble_application_win64 #(target, configuration)
build/$1/$2-win64/${TARGET_NAME_$1}.exe: ${THIN_BINARIES_$1_$2_win64} ${RESOURCES_$1} ${RESOURCES_$1_win64} | build/$1/$2-win64
${call copy_target_resources,$1,win64,build/$1/$2-win64/Resources}
cp ${THIN_BINARIES_$1_$2_win64} "$$@"
endef
#Produces final application build targets
${foreach target,${APPLICATION_TARGETS}, \
${foreach configuration,${CONFIGURATIONS_${target}}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval ${call assemble_application_${platform},${target},${configuration}}} \
} \
} \
}
define library_dependency_template #(target, configuration, platform)
${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$1_$3} build/$1/$2-$3/${TARGET_NAME_$1}.a
endef
define executable_dependency_template #(target, configuration, platform)
${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$1_$3} build/$1/$2-$3/${TARGET_NAME_$1}${EXECUTABLE_SUFFIX_$3}
endef
define application_dependency_template #(target, configuration, platform)
${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_$1} ${STEM_SOURCE_DEPENDENCIES_$1_$3} build/$1/$2-$3/${call application_file_template_$3,$1}
endef
define application_file_template_macosx #(target)
$${HUMAN_READABLE_TARGET_NAME_$1}.app/Contents/MacOS/${TARGET_NAME_$1}
endef
define application_file_template_iphonesimulator #(target)
${TARGET_NAME_$1}.app/${TARGET_NAME_$1}
endef
define application_file_template_iphoneos #(target)
${TARGET_NAME_$1}.app/${TARGET_NAME_$1}
endef
define application_file_template_linux32 #(target)
${TARGET_NAME_$1}
endef
define application_file_template_linux64 #(target)
${TARGET_NAME_$1}
endef
define application_file_template_win32 #(target)
${TARGET_NAME_$1}.exe
endef
define application_file_template_win64 #(target)
${TARGET_NAME_$1}.exe
endef
define target_template #(target, target_type)
.PHONY: $1
$1: ${foreach configuration,${CONFIGURATIONS_$1},${foreach platform,${PLATFORMS_$1},${call $2_dependency_template,$1,${configuration},${platform}}}}
endef
${foreach target,${LIBRARY_TARGETS}, \
${eval ${call target_template,${target},library}} \
}
${foreach target,${EXECUTABLE_TARGETS}, \
${eval ${call target_template,${target},executable}} \
}
${foreach target,${APPLICATION_TARGETS}, \
${eval ${call target_template,${target},application}} \
}
.PHONY: test
test: ${foreach platform,${PLATFORMS_unittest},run_unittests_${platform}}
.PHONY: run_unittests_macosx
run_unittests_macosx: unittest
./build/unittest/debug-macosx/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-macosx"
.PHONY: run_unittests_iphonesimulator
run_unittests_iphonesimulator: unittest
DYLD_ROOT_PATH=${SDKROOT_iphonesimulator} \
./build/unittest/debug-iphonesimulator/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-iphonesimulator"
.PHONY: run_unittests_linux32
run_unittests_linux32: unittest
./build/unittest/debug-linux32/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-linux32"
.PHONY: run_unittests_linux64
run_unittests_linux64: unittest
./build/unittest/debug-linux64/${TARGET_NAME_unittest} "${CURDIR}/build/unittest/debug-linux64"
.PHONY: run_unittests_win32
run_unittests_win32: unittest
./build/unittest/debug-win32/${TARGET_NAME_unittest}.exe "${CURDIR}/build/unittest/debug-win32"
.PHONY: run_unittests_win64
run_unittests_win64: unittest
./build/unittest/debug-win64/${TARGET_NAME_unittest}.exe "${CURDIR}/build/unittest/debug-win64"
define analyze_file_template_clang #(target, platform, file)
build/analyzer-results/clang-$1-$2/${basename ${notdir $3}}.txt: $3 ${PREREQS_$1} | build/analyzer-results/clang-$1-$2
${CLANG_$2} --analyze ${call include_ccflags_template,$1,$2} ${call define_ccflags_template,$1,analyze,$2,none} ${CLANGFLAGS} ${CLANGFLAGS_$1} ${CLANGFLAGS_$2} -o $${basename $$@}.plist $3 > $$@ 2>&1; true
@cat $$@
endef
define analyze_file_template_splint #(target, platform, file)
build/analyzer-results/splint-$1-$2/${basename ${notdir $3}}.txt: $3 ${PREREQS_$1} | build/analyzer-results/splint-$1-$2
${SPLINT_$2} ${call include_ccflags_template,$1,$2} ${call define_ccflags_template,$1,analyze,$2,none} ${SPLINTFLAGS} ${SPLINTFLAGS_$1} ${SPLINTFLAGS_$2} $3 > $$@ 2>&1; true
@cat $$@
endef
define analyzed_sources_template #(analyzer, target, platform)
${sort ${filter-out ${ANALYZER_EXCLUDE_SOURCES_$1},${SOURCES_$2} ${SOURCES_$2_$3}}}
endef
define analyzer_output_template #(analyzer, target, platform)
${foreach file,${call analyzed_sources_template,$1,$2,$3}, \
build/analyzer-results/$1-$2-$3/${basename ${notdir ${file}}}.txt \
}
endef
define analyze_target_template #(analyzer, target, platform)
.PHONY: analyze_$1_$2_$3
analyze_$1_$2_$3: ${call analyzer_output_template,$1,$2,$3}
endef
define analyze_template #(analyzer)
.PHONY: analyze_$1
analyze_$1: ${foreach target,${TARGETS},${foreach platform,${PLATFORMS_${target}},analyze_$1_${target}_${platform}}}
endef
${foreach analyzer,${ANALYZERS}, \
${eval ${call analyze_template,${analyzer}}} \
${foreach target,${TARGETS}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval ${call analyze_target_template,${analyzer},${target},${platform}}} \
${foreach file,${call analyzed_sources_template,${analyzer},${target},${platform}}, \
${eval ${call analyze_file_template_${analyzer},${target},${platform},${file}}} \
} \
} \
} \
}
${foreach analyzer,${ANALYZERS}, \
${foreach target,${TARGETS}, \
${foreach platform,${PLATFORMS_${target}}, \
${eval ${call create_directory_target_template,build/analyzer-results/${analyzer}-${target}-${platform}}} \
} \
} \
}
.PHONY: analyze
analyze: ${foreach analyzer,${ANALYZERS},analyze_${analyzer}}
${foreach dir,${sort ${foreach include_file,${INCLUDES},build/include/${notdir ${patsubst %/,%,${dir ${include_file}}}}}}, \
${eval ${call create_directory_target_template,${dir}}} \
}
.PHONY: include
include: ${INCLUDES} | ${foreach include_file,${INCLUDES},build/include/${notdir ${patsubst %/,%,${dir ${include_file}}}}}
${foreach include_file,${INCLUDES}, \
cp ${include_file} build/include/${notdir ${patsubst %/,%,${dir ${include_file}}}}${newline_and_tab} \
}
.PHONY: clean
clean:
rm -rf build
${foreach dependency,${sort ${foreach target,${TARGETS},${foreach platform,${PLATFORMS_${target}},${STEM_SOURCE_DEPENDENCIES} ${STEM_SOURCE_DEPENDENCIES_${target}} ${STEM_SOURCE_DEPENDENCIES_${platform}} ${STEM_SOURCE_DEPENDENCIES_${target}_${platform}}}}}, \
${MAKE} -C dep/${word 1,${subst /, ,${dependency}}} clean${newline_and_tab} \
}
TARGET_SUFFIX_ipad = _ipad
TARGET_SUFFIX_iphone4 = _iphone4
IPHONE_SDK_VERSION_iphone ?= 4.2
IPHONE_SDK_VERSION_ipad ?= 3.2
IPHONE_SDK_VERSION_iphone4 ?= 4.2
IPHONESIMULATOR_APPLICATIONS_DIR_iphone ?= ${HOME}/Library/Application Support/iPhone Simulator/${IPHONE_SDK_VERSION_iphone}/Applications
IPHONESIMULATOR_APPLICATIONS_DIR_ipad ?= ${HOME}/Library/Application Support/iPhone Simulator/${IPHONE_SDK_VERSION_ipad}/Applications
IPHONESIMULATOR_APPLICATIONS_DIR_iphone4 ?= ${HOME}/Library/Application Support/iPhone Simulator/${IPHONE_SDK_VERSION_iphone4}/Applications
SIMULATE_DEVICE_iphone = iPhone
SIMULATE_DEVICE_ipad = iPad
SIMULATE_DEVICE_iphone4 = iPhone 4
SIMULATE_SDKROOT_iphone = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDK_VERSION_iphone}.sdk
SIMULATE_SDKROOT_ipad = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDK_VERSION_ipad}.sdk
SIMULATE_SDKROOT_iphone4 = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDK_VERSION_iphone4}.sdk
IPHONE_SIMULATOR_PATH ?= /Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app
define install_target_iphonesimulator_template #(target, simulate_device)
.PHONY: install_$1_iphonesimulator${TARGET_SUFFIX_$2}
install_$1_iphonesimulator${TARGET_SUFFIX_$2}: $1
killall "iPhone Simulator"; true
rm -rf "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}"
mkdir -p "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}/Documents"
mkdir -p "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}/Library/Preferences"
mkdir -p "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}/tmp"
cp -r "build/$1/debug-iphonesimulator/${TARGET_NAME_$1}.app" "${IPHONESIMULATOR_APPLICATIONS_DIR_$2}/${TARGET_NAME_$1}"
defaults write com.apple.iphonesimulator SimulateDevice -string "${SIMULATE_DEVICE_$2}"
defaults write com.apple.iphonesimulator SimulateSDKRoot -string "${SIMULATE_SDKROOT_$2}"
defaults write com.apple.iphonesimulator currentSDKRoot -string "${SIMULATE_SDKROOT_$2}"
open "${IPHONE_SIMULATOR_PATH}"
endef
define add_blob_header #(source_file, target_file)
ruby -e "contents = \"\"; File.open(\"$1\", \"r\") {|file| contents = file.read}; File.open(\"$2\", \"w\") {|file| file.write(\"\xFA\xDE\x71\x71\"); file.write([contents.length + 8].pack(\"N\")); file.write(contents)}"
endef
RESOURCE_RULES_PLIST = /Developer/Platforms/MacOSX.platform/ResourceRules.plist
define codesign_target_iphoneos_template #(target)
.PHONY: codesign_$1_iphoneos
codesign_$1_iphoneos: $1
sed -e "s/\$$$${PRODUCT_NAME}/${TARGET_NAME_$1}/g" resources/Entitlements.plist > build/intermediate/Entitlements.plist
${call add_blob_header,build/intermediate/Entitlements.plist,build/intermediate/Entitlements.xcent}
export CODESIGN_ALLOCATE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate
${foreach configuration,${CONFIGURATIONS_$1},\
cp "${RESOURCE_RULES_PLIST}" "build/$1/${configuration}-iphoneos/${TARGET_NAME_$1}.app"${newline_and_tab} \
/usr/bin/codesign -f -s ${CODESIGN_IDENTITY} --resource-rules=${RESOURCE_RULES_PLIST} --entitlements=build/intermediate/Entitlements.xcent "build/$1/${configuration}-iphoneos/${TARGET_NAME_$1}.app"${newline_and_tab} \
}
endef
${foreach target,${APPLICATION_TARGETS}, \
${eval ${call install_target_iphonesimulator_template,${target},iphone}} \
${eval ${call install_target_iphonesimulator_template,${target},ipad}} \
${eval ${call install_target_iphonesimulator_template,${target},iphone4}} \
${eval ${call codesign_target_iphoneos_template,${target}}} \
}
INSTALL_DIR = ${STEM_SHARED_DIR}/${PROJECT_NAME}/${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_TWEAK}
.PHONY: install
install:
mkdir -p "${INSTALL_DIR}/include" "${INSTALL_DIR}/library" "${INSTALL_DIR}/testharness"
cp Changes.txt License.txt ReadMe.txt version ${INSTALL_DIR}
cp -r build/include/* ${INSTALL_DIR}/include
cp -r build/library/* ${INSTALL_DIR}/library
cp -r build/testharness/* ${INSTALL_DIR}/testharness

View file

@ -1,5 +0,0 @@
Gamepad provides a low-level interface for USB game controller input. Each element on an attached game controller is mapped to zero or more buttons and zero or more axes. Buttons are binary controls; axes are continuous values ranging from -1.0f to 1.0f. The presence and ordering of elements depends on the platform and driver.
Typical usage: Register a callback to notify you when a new device is attached with Gamepad_deviceAttachFunc(), then call Gamepad_init() and Gamepad_detectDevices(). Your callback will be called once per connected game controller. Also register callbacks for button and axis events with Gamepad_buttonDownFunc(), Gamepad_buttonUpFunc(), and Gamepad_axisMoveFunc(). Call Gamepad_processEvents() every frame, and Gamepad_detectDevices() occasionally to be notified of new devices that were plugged in after your Gamepad_init() call. If you're interested in knowing when a device was disconnected, you can call Gamepad_deviceRemoveFunc() get be notified of it.
See Gamepad.h for more details.

View file

@ -1,162 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>testharness</RootNamespace>
<ProjectName>testharness</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\source;C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Projects\StemLibProjects\gamepad_trunk\msvc\Debug;C:\Program Files\Common Files\MSVC\freeglut\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<AdditionalDependencies>libstem_gamepad.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\source;C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Projects\StemLibProjects\gamepad_trunk\msvc\x64\Debug;C:\Program Files\Common Files\MSVC\freeglut\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
<AdditionalDependencies>libstem_gamepad.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>C:\Program Files\Common Files\MSVC\freeglut\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Common Files\MSVC\freeglut\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>C:\Program Files\Common Files\MSVC\freeglut\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\source\testharness\TestHarness_main.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\source\testharness\TestHarness_main.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -1,49 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2013 for Windows Desktop
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libstem_gamepad", "libstem_gamepad\libstem_gamepad.vcxproj", "{4579D2EA-39EB-4766-9EB6-497500F379D6}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testharness", "Win32Project1\Win32Project1.vcxproj", "{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}"
ProjectSection(ProjectDependencies) = postProject
{4579D2EA-39EB-4766-9EB6-497500F379D6} = {4579D2EA-39EB-4766-9EB6-497500F379D6}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Mixed Platforms.ActiveCfg = Debug|x64
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Mixed Platforms.Build.0 = Debug|x64
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Win32.ActiveCfg = Debug|Win32
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|Win32.Build.0 = Debug|Win32
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|x64.ActiveCfg = Debug|x64
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Debug|x64.Build.0 = Debug|x64
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Mixed Platforms.Build.0 = Release|Win32
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Win32.ActiveCfg = Release|Win32
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|Win32.Build.0 = Release|Win32
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|x64.ActiveCfg = Release|x64
{4579D2EA-39EB-4766-9EB6-497500F379D6}.Release|x64.Build.0 = Release|x64
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Mixed Platforms.ActiveCfg = Debug|x64
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Mixed Platforms.Build.0 = Debug|x64
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Win32.ActiveCfg = Debug|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|Win32.Build.0 = Debug|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Debug|x64.ActiveCfg = Debug|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Mixed Platforms.Build.0 = Release|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Win32.ActiveCfg = Release|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|Win32.Build.0 = Release|Win32
{EAF0DEE7-ADEF-4778-B4CD-8DF22FD0F724}.Release|x64.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -1,142 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{4579D2EA-39EB-4766-9EB6-497500F379D6}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libstem_gamepad</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../source</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../source</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\source\gamepad\Gamepad_private.c" />
<ClCompile Include="..\..\source\gamepad\Gamepad_windows_dinput.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\source\gamepad\Gamepad.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\source\gamepad\Gamepad_windows_dinput.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\source\gamepad\Gamepad_private.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\source\gamepad\Gamepad.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//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>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${PRODUCT_NAME} ${VERSION} Copyright © ${COPYRIGHT_YEAR} Alex Diener
http://sacredsoftware.net/</string>
<key>CFBundleIdentifier</key>
<string>com.sacredsoftware.${PRODUCT_NAME}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>${PRODUCT_NAME} ${VERSION}</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © ${COPYRIGHT_YEAR} Alex Diener
http://sacredsoftware.net/</string>
</dict>
</plist>

View file

@ -1,400 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <regstr.h>
struct Gamepad_devicePrivate {
UINT joystickID;
JOYINFOEX lastState;
int xAxisIndex;
int yAxisIndex;
int zAxisIndex;
int rAxisIndex;
int uAxisIndex;
int vAxisIndex;
int povXAxisIndex;
int povYAxisIndex;
UINT (* axisRanges)[2];
};
static struct Gamepad_device ** devices = NULL;
static unsigned int numDevices = 0;
static unsigned int nextDeviceID = 0;
static bool inited = false;
void Gamepad_init() {
if (!inited) {
inited = true;
Gamepad_detectDevices();
}
}
static void disposeDevice(struct Gamepad_device * deviceRecord) {
free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisRanges);
free(deviceRecord->privateData);
free((void *) deviceRecord->description);
free(deviceRecord->axisStates);
free(deviceRecord->buttonStates);
free(deviceRecord);
}
void Gamepad_shutdown() {
unsigned int deviceIndex;
if (inited) {
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
disposeDevice(devices[deviceIndex]);
}
free(devices);
devices = NULL;
numDevices = 0;
inited = false;
}
}
unsigned int Gamepad_numDevices() {
return numDevices;
}
struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) {
if (deviceIndex >= numDevices) {
return NULL;
}
return devices[deviceIndex];
}
#define REG_STRING_MAX 256
static char * getDeviceDescription(UINT joystickID, JOYCAPS caps) {
char * description = NULL;
char subkey[REG_STRING_MAX];
HKEY topKey, key;
LONG result;
snprintf(subkey, REG_STRING_MAX, "%s\\%s\\%s", REGSTR_PATH_JOYCONFIG, caps.szRegKey, REGSTR_KEY_JOYCURR);
result = RegOpenKeyEx(topKey = HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &key);
if (result != ERROR_SUCCESS) {
result = RegOpenKeyEx(topKey = HKEY_CURRENT_USER, subkey, 0, KEY_READ, &key);
}
if (result == ERROR_SUCCESS) {
char value[REG_STRING_MAX];
char name[REG_STRING_MAX];
DWORD nameSize;
snprintf(value, REG_STRING_MAX, "Joystick%d%s", joystickID + 1, REGSTR_VAL_JOYOEMNAME);
nameSize = sizeof(name);
result = RegQueryValueEx(key, value, NULL, NULL, (LPBYTE) name, &nameSize);
RegCloseKey(key);
if (result == ERROR_SUCCESS) {
snprintf(subkey, REG_STRING_MAX, "%s\\%s", REGSTR_PATH_JOYOEM, name);
result = RegOpenKeyEx(topKey, subkey, 0, KEY_READ, &key);
if (result == ERROR_SUCCESS) {
nameSize = sizeof(name);
result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, NULL, &nameSize);
if (result == ERROR_SUCCESS) {
description = malloc(nameSize);
result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, (LPBYTE) description, &nameSize);
}
RegCloseKey(key);
if (result == ERROR_SUCCESS) {
return description;
}
free(description);
}
}
}
description = malloc(strlen(caps.szPname) + 1);
strcpy(description, caps.szPname);
return description;
}
void Gamepad_detectDevices() {
unsigned int numPadsSupported;
unsigned int deviceIndex, deviceIndex2;
JOYINFOEX info;
JOYCAPS caps;
bool duplicate;
struct Gamepad_device * deviceRecord;
struct Gamepad_devicePrivate * deviceRecordPrivate;
UINT joystickID;
int axisIndex;
if (!inited) {
return;
}
numPadsSupported = joyGetNumDevs();
for (deviceIndex = 0; deviceIndex < numPadsSupported; deviceIndex++) {
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
joystickID = JOYSTICKID1 + deviceIndex;
if (joyGetPosEx(joystickID, &info) == JOYERR_NOERROR &&
joyGetDevCaps(joystickID, &caps, sizeof(JOYCAPS)) == JOYERR_NOERROR) {
duplicate = false;
for (deviceIndex2 = 0; deviceIndex2 < numDevices; deviceIndex2++) {
if (((struct Gamepad_devicePrivate *) devices[deviceIndex2]->privateData)->joystickID == joystickID) {
duplicate = true;
break;
}
}
if (duplicate) {
continue;
}
deviceRecord = malloc(sizeof(struct Gamepad_device));
deviceRecord->deviceID = nextDeviceID++;
deviceRecord->description = getDeviceDescription(joystickID, caps);
deviceRecord->vendorID = caps.wMid;
deviceRecord->productID = caps.wPid;
deviceRecord->numAxes = caps.wNumAxes + ((caps.wCaps & JOYCAPS_HASPOV) ? 2 : 0);
deviceRecord->numButtons = caps.wNumButtons;
deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes);
deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons);
devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1));
devices[numDevices++] = deviceRecord;
deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate));
deviceRecordPrivate->joystickID = joystickID;
deviceRecordPrivate->lastState = info;
deviceRecordPrivate->xAxisIndex = 0;
deviceRecordPrivate->yAxisIndex = 1;
axisIndex = 2;
deviceRecordPrivate->zAxisIndex = (caps.wCaps & JOYCAPS_HASZ) ? axisIndex++ : -1;
deviceRecordPrivate->rAxisIndex = (caps.wCaps & JOYCAPS_HASR) ? axisIndex++ : -1;
deviceRecordPrivate->uAxisIndex = (caps.wCaps & JOYCAPS_HASU) ? axisIndex++ : -1;
deviceRecordPrivate->vAxisIndex = (caps.wCaps & JOYCAPS_HASV) ? axisIndex++ : -1;
deviceRecordPrivate->axisRanges = malloc(sizeof(UINT[2]) * axisIndex);
deviceRecordPrivate->axisRanges[0][0] = caps.wXmin;
deviceRecordPrivate->axisRanges[0][1] = caps.wXmax;
deviceRecordPrivate->axisRanges[1][0] = caps.wYmin;
deviceRecordPrivate->axisRanges[1][1] = caps.wYmax;
if (deviceRecordPrivate->zAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][0] = caps.wZmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][1] = caps.wZmax;
}
if (deviceRecordPrivate->rAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][0] = caps.wRmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][1] = caps.wRmax;
}
if (deviceRecordPrivate->uAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][0] = caps.wUmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][1] = caps.wUmax;
}
if (deviceRecordPrivate->vAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][0] = caps.wVmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][1] = caps.wVmax;
}
deviceRecordPrivate->povXAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1;
deviceRecordPrivate->povYAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1;
deviceRecord->privateData = deviceRecordPrivate;
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext);
}
}
}
}
static double currentTime() {
// HACK: No timestamp data from joyGetInfoEx, so we make it up
static LARGE_INTEGER frequency;
LARGE_INTEGER currentTime;
if (frequency.QuadPart == 0) {
QueryPerformanceFrequency(&frequency);
}
QueryPerformanceCounter(&currentTime);
return (double) currentTime.QuadPart / frequency.QuadPart;
}
static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWORD ivalue) {
float value, lastValue;
struct Gamepad_devicePrivate * devicePrivate;
if (axisIndex < 0 || axisIndex >= (int) device->numAxes) {
return;
}
devicePrivate = device->privateData;
value = (ivalue - devicePrivate->axisRanges[axisIndex][0]) / (float) (devicePrivate->axisRanges[axisIndex][1] - devicePrivate->axisRanges[axisIndex][0]) * 2.0f - 1.0f;
lastValue = device->axisStates[axisIndex];
device->axisStates[axisIndex] = value;
if (Gamepad_axisMoveCallback != NULL) {
Gamepad_axisMoveCallback(device, axisIndex, value, lastValue, currentTime(), Gamepad_axisMoveContext);
}
}
static void handleButtonChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) {
bool down;
unsigned int buttonIndex;
for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) {
if ((lastValue ^ value) & (1 << buttonIndex)) {
down = !!(value & (1 << buttonIndex));
device->buttonStates[buttonIndex] = down;
if (down && Gamepad_buttonDownCallback != NULL) {
Gamepad_buttonDownCallback(device, buttonIndex, currentTime(), Gamepad_buttonDownContext);
} else if (!down && Gamepad_buttonUpCallback != NULL) {
Gamepad_buttonUpCallback(device, buttonIndex, currentTime(), Gamepad_buttonUpContext);
}
}
}
}
static void povToXY(DWORD pov, int * outX, int * outY) {
if (pov == JOY_POVCENTERED) {
*outX = *outY = 0;
} else {
if (pov > JOY_POVFORWARD && pov < JOY_POVBACKWARD) {
*outX = 1;
} else if (pov > JOY_POVBACKWARD) {
*outX = -1;
} else {
*outX = 0;
}
if (pov > JOY_POVLEFT || pov < JOY_POVRIGHT) {
*outY = -1;
} else if (pov > JOY_POVRIGHT && pov < JOY_POVLEFT) {
*outY = 1;
} else {
*outY = 0;
}
}
}
static void handlePOVChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) {
struct Gamepad_devicePrivate * devicePrivate;
int lastX, lastY, newX, newY;
devicePrivate = device->privateData;
if (devicePrivate->povXAxisIndex == -1 || devicePrivate->povYAxisIndex == -1) {
return;
}
povToXY(lastValue, &lastX, &lastY);
povToXY(value, &newX, &newY);
if (newX != lastX) {
device->axisStates[devicePrivate->povXAxisIndex] = newX;
if (Gamepad_axisMoveCallback != NULL) {
Gamepad_axisMoveCallback(device, devicePrivate->povXAxisIndex, newX, lastX, currentTime(), Gamepad_axisMoveContext);
}
}
if (newY != lastY) {
device->axisStates[devicePrivate->povYAxisIndex] = newY;
if (Gamepad_axisMoveCallback != NULL) {
Gamepad_axisMoveCallback(device, devicePrivate->povYAxisIndex, newY, lastY, currentTime(), Gamepad_axisMoveContext);
}
}
}
void Gamepad_processEvents() {
unsigned int deviceIndex;
static bool inProcessEvents;
JOYINFOEX info;
MMRESULT result;
struct Gamepad_device * device;
struct Gamepad_devicePrivate * devicePrivate;
if (!inited || inProcessEvents) {
return;
}
inProcessEvents = true;
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
device = devices[deviceIndex];
devicePrivate = device->privateData;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
result = joyGetPosEx(devicePrivate->joystickID, &info);
if (result == JOYERR_UNPLUGGED) {
if (Gamepad_deviceRemoveCallback != NULL) {
Gamepad_deviceRemoveCallback(device, Gamepad_deviceRemoveContext);
}
disposeDevice(device);
numDevices--;
for (; deviceIndex < numDevices; deviceIndex++) {
devices[deviceIndex] = devices[deviceIndex + 1];
}
} else if (result == JOYERR_NOERROR) {
if (info.dwXpos != devicePrivate->lastState.dwXpos) {
handleAxisChange(device, devicePrivate->xAxisIndex, info.dwXpos);
}
if (info.dwYpos != devicePrivate->lastState.dwYpos) {
handleAxisChange(device, devicePrivate->yAxisIndex, info.dwYpos);
}
if (info.dwZpos != devicePrivate->lastState.dwZpos) {
handleAxisChange(device, devicePrivate->zAxisIndex, info.dwZpos);
}
if (info.dwRpos != devicePrivate->lastState.dwRpos) {
handleAxisChange(device, devicePrivate->rAxisIndex, info.dwRpos);
}
if (info.dwUpos != devicePrivate->lastState.dwUpos) {
handleAxisChange(device, devicePrivate->uAxisIndex, info.dwUpos);
}
if (info.dwVpos != devicePrivate->lastState.dwVpos) {
handleAxisChange(device, devicePrivate->vAxisIndex, info.dwVpos);
}
if (info.dwPOV != devicePrivate->lastState.dwPOV) {
handlePOVChange(device, devicePrivate->lastState.dwPOV, info.dwPOV);
}
if (info.dwButtons != devicePrivate->lastState.dwButtons) {
handleButtonChange(device, devicePrivate->lastState.dwButtons, info.dwButtons);
}
devicePrivate->lastState = info;
}
}
inProcessEvents = false;
}

View file

@ -1,214 +0,0 @@
#include "gamepad/Gamepad.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#else
#include <GL/glut.h>
#include <GL/gl.h>
#endif
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
static bool verbose = false;
void onButtonDown(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) {
if (verbose) {
printf("Button %u down on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context);
}
}
void onButtonUp(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) {
if (verbose) {
printf("Button %u up on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context);
}
}
void onAxisMoved(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context) {
if (verbose) {
printf("Axis %u moved from %f to %f on device %u at %f with context %p\n", axisID, lastValue, value, device->deviceID, timestamp, context);
}
}
void onDeviceAttached(struct Gamepad_device * device, void * context) {
if (verbose) {
printf("Device ID %u attached (vendor = 0x%X; product = 0x%X) with context %p\n", device->deviceID, device->vendorID, device->productID, context);
}
}
void onDeviceRemoved(struct Gamepad_device * device, void * context) {
if (verbose) {
printf("Device ID %u removed with context %p\n", device->deviceID, context);
}
}
static unsigned int windowWidth = 800, windowHeight = 600;
static void initGamepad() {
Gamepad_deviceAttachFunc(onDeviceAttached, (void *) 0x1);
Gamepad_deviceRemoveFunc(onDeviceRemoved, (void *) 0x2);
Gamepad_buttonDownFunc(onButtonDown, (void *) 0x3);
Gamepad_buttonUpFunc(onButtonUp, (void *) 0x4);
Gamepad_axisMoveFunc(onAxisMoved, (void *) 0x5);
Gamepad_init();
}
static void drawGlutString(int rasterPosX, int rasterPosY, const char * string) {
size_t length, charIndex;
glRasterPos2i(rasterPosX, rasterPosY);
length = strlen(string);
for (charIndex = 0; charIndex < length; charIndex++) {
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, string[charIndex]);
}
}
#define POLL_ITERATION_INTERVAL 30
static void displayFunc(void) {
unsigned int gamepadIndex;
struct Gamepad_device * device;
unsigned int axesPerRow, buttonsPerRow;
unsigned int axisRowIndex, axisIndex;
unsigned int buttonRowIndex, buttonIndex;
float axisState;
char indexString[16];
static unsigned int iterationsToNextPoll = POLL_ITERATION_INTERVAL;
char descriptionString[256];
iterationsToNextPoll--;
if (iterationsToNextPoll == 0) {
Gamepad_detectDevices();
iterationsToNextPoll = POLL_ITERATION_INTERVAL;
}
Gamepad_processEvents();
axesPerRow = (windowWidth - 10) / 60;
buttonsPerRow = (windowWidth - 10) / 30;
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glTranslatef(5.0f, 20.0f, 0.0f);
for (gamepadIndex = 0; gamepadIndex < Gamepad_numDevices(); gamepadIndex++) {
device = Gamepad_deviceAtIndex(gamepadIndex);
glColor3f(0.0f, 0.0f, 0.0f);
snprintf(descriptionString, 256, "%s (0x%X 0x%X %u)", device->description, device->vendorID, device->productID, device->deviceID);
drawGlutString(0, 0, descriptionString);
for (axisRowIndex = 0; axisRowIndex <= device->numAxes / axesPerRow; axisRowIndex++) {
glPushMatrix();
for (axisIndex = axisRowIndex * axesPerRow; axisIndex < (axisRowIndex + 1) * axesPerRow && axisIndex < device->numAxes; axisIndex++) {
axisState = device->axisStates[axisIndex];
sprintf(indexString, "a%d", axisIndex);
glColor3f(0.0f, 0.0f, 0.0f);
drawGlutString(2, 28, indexString);
glBegin(GL_QUADS);
glVertex2f(2.0f, 5.0f);
glVertex2f(58.0f, 5.0f);
glVertex2f(58.0f, 15.0f);
glVertex2f(2.0f, 15.0f);
glColor3f(0.5f, 1.0f, 0.5f);
glVertex2f(29.0f + axisState * 26, 6.0f);
glVertex2f(31.0f + axisState * 26, 6.0f);
glVertex2f(31.0f + axisState * 26, 14.0f);
glVertex2f(29.0f + axisState * 26, 14.0f);
glEnd();
glTranslatef(60.0f, 0.0f, 0.0f);
}
glPopMatrix();
glTranslatef(0.0f, 32.0f, 0.0f);
}
for (buttonRowIndex = 0; buttonRowIndex <= device->numButtons / buttonsPerRow; buttonRowIndex++) {
glPushMatrix();
for (buttonIndex = buttonRowIndex * buttonsPerRow; buttonIndex < (buttonRowIndex + 1) * buttonsPerRow && buttonIndex < device->numButtons; buttonIndex++) {
sprintf(indexString, "b%d", buttonIndex);
glColor3f(0.0f, 0.0f, 0.0f);
drawGlutString(2, 32, indexString);
glBegin(GL_QUADS);
glColor3f(0.0f, 0.0f, 0.0f);
glVertex2f(2.0f, 2.0f);
glVertex2f(28.0f, 2.0f);
glVertex2f(28.0f, 18.0f);
glVertex2f(2.0f, 18.0f);
if (device->buttonStates[buttonIndex]) {
glColor3f(0.5f, 1.0f, 0.5f);
glVertex2f(3.0f, 3.0f);
glVertex2f(27.0f, 3.0f);
glVertex2f(27.0f, 17.0f);
glVertex2f(3.0f, 17.0f);
}
glEnd();
glTranslatef(30.0f, 0.0f, 0.0f);
}
glPopMatrix();
glTranslatef(0.0f, 38.0f, 0.0f);
}
glTranslatef(0.0f, 40.0f, 0.0f);
}
if (gamepadIndex == 0) {
glLoadIdentity();
glTranslatef(5.0f, 20.0f, 0.0f);
glColor3f(0.0f, 0.0f, 0.0f);
drawGlutString(0, 0, "No devices found; plug in a USB gamepad and it will be detected automatically");
}
glutSwapBuffers();
glutPostRedisplay();
}
static void keyDownFunc(unsigned char charCode, int x, int y) {
if (charCode == 'r') {
Gamepad_shutdown();
initGamepad();
}
}
static void reshapeFunc(int newWidth, int newHeight) {
windowWidth = newWidth;
windowHeight = newHeight;
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char ** argv) {
int argIndex;
for (argIndex = 1; argIndex < argc; argIndex++) {
if (!strcmp(argv[argIndex], "-v")) {
verbose = true;
}
}
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(30, 30);
glutInitWindowSize(800, 600);
glutCreateWindow("Gamepad Test Harness");
glutReshapeFunc(reshapeFunc);
glutDisplayFunc(displayFunc);
glutKeyboardFunc(keyDownFunc);
initGamepad();
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glutMainLoop();
}

View file

@ -1,3 +0,0 @@
VERSION_MAJOR=1
VERSION_MINOR=4
VERSION_TWEAK=0

View file

@ -1,276 +1,252 @@
#include "src/gamepad/Gamepad.h"
/*
struct Gamepad_device {
// Unique device identifier for application session, starting at 0 for the first device attached and
// incrementing by 1 for each additional device. If a device is removed and subsequently reattached
// during the same application session, it will have a new deviceID.
unsigned int deviceID;
// Human-readable device name
const char * description;
// USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented.
int vendorID;
int productID;
// Number of axis elements belonging to the device
unsigned int numAxes;
// Number of button elements belonging to the device
unsigned int numButtons;
// Array[numAxes] of values representing the current state of each axis, in the range [-1..1]
float * axisStates;
// Array[numButtons] of values representing the current state of each button
bool * buttonStates;
// Platform-specific device data storage. Don't touch unless you know what you're doing and don't
// mind your code breaking in future versions of this library.
void * privateData;
};
*/
bool verbose = false;
char *verboseMessage = (char *)malloc(1000);
void onButtonDown(struct Gamepad_device *device, unsigned int buttonID, double timestamp, void *context) {
// buttonId is base 0
if (verbose) {
sprintf(verboseMessage, "Button %u down on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context);
std::cout << verboseMessage;
}
int button = buttonID;
int32 di, controller;
controller = 0;
for (di = 1; di <= device_last; di++) {
static device_struct *d;
d = &devices[di];
if (d->used == 1) {
if (d->type == 1) {
controller++;
if (d->handle_int == device->deviceID) {
// ON STRIG event
static int32 i;
if (controller <= 256 && button <= 255) { // within supported range
i = (controller - 1) * 256 + button;
if (onstrig[i].active) {
if (onstrig[i].id) {
if (onstrig[i].active == 1) { //(1)ON
onstrig[i].state++;
} else { //(2)STOP
onstrig[i].state = 1;
}
qbevent = 1;
}
}
} // within supported range
int32 eventIndex = createDeviceEvent(d);
setDeviceEventButtonValue(d, eventIndex, button, 1);
commitDeviceEvent(d);
// set STRIG_button_pressed for button
if (button >= 0 && button <= 255) {
d->STRIG_button_pressed[button] = 1;
}
} // js index
} // type==1
} // used
} // di
}
void onButtonUp(struct Gamepad_device *device, unsigned int buttonID, double timestamp, void *context) {
if (verbose) {
sprintf(verboseMessage, "Button %u up on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context);
std::cout << verboseMessage;
}
int button = buttonID;
int32 di;
for (di = 1; di <= device_last; di++) {
static device_struct *d;
d = &devices[di];
if (d->used == 1) {
if (d->type == 1) {
if (d->handle_int == device->deviceID) {
int32 eventIndex = createDeviceEvent(d);
setDeviceEventButtonValue(d, eventIndex, button, 0);
commitDeviceEvent(d);
} // js index
} // type==1
} // used
} // di
}
void onAxisMoved(struct Gamepad_device *device, unsigned int axisID, float value, float lastValue, double timestamp, void *context) {
if (verbose) {
sprintf(verboseMessage, "Axis %u moved from %f to %f on device %u at %f with context %p\n", axisID, lastValue, value, device->deviceID, timestamp,
context);
std::cout << verboseMessage;
}
int axis = axisID;
int32 di;
for (di = 1; di <= device_last; di++) {
static device_struct *d;
d = &devices[di];
if (d->used == 1) {
if (d->type == 1) {
if (d->handle_int == device->deviceID) {
float f;
f = value;
/*
if (f==-32768) f=-32767;
f/=32767.0;
*/
if (f > 1.0)
f = 1.0;
if (f < -1.0)
f = -1.0;
int32 eventIndex = createDeviceEvent(d);
setDeviceEventAxisValue(d, eventIndex, axis, f);
commitDeviceEvent(d);
} // js index
} // type==1
} // used
} // di
}
void onDeviceAttached(struct Gamepad_device *device, void *context) {
if (verbose) {
sprintf(verboseMessage, "Device ID %u attached (vendor = 0x%X; product = 0x%X) with context %p\n", device->deviceID, device->vendorID,
device->productID, context);
std::cout << verboseMessage;
}
int i, x, x2;
// re-aquire a potentially dropped device in its original index
for (i = 1; i <= device_last; i++) {
if (devices[i].used) {
if (devices[i].type == 1) { // it's a joystick/gamepad
if (!devices[i].connected) {
if (device->productID == devices[i].product_id) {
if (device->vendorID == devices[i].vendor_id) {
if (device->numAxes == devices[i].axes) {
if (device->numButtons == devices[i].buttons) {
//(sometimes when gamepads are re-plugged they receieve a generic name)
// if (strlen(device->description)==strlen(devices[i].description)){//same name length
// if (strcmp(device->description,devices[i].description)==0){//same name content
// re-acquire device
devices[i].handle_int = device->deviceID;
if (!devices[i].connected) {
devices[i].connected = 1;
devices[i].name[strlen(devices[i].name) - 14] = 0; // Remove "[DISCONNECTED]"
}
devices[i].used = 1;
return;
//}
//}
}
}
}
}
}
}
}
} // i
// add new device
i = device_last + 1;
if (i > device_max) {
device_struct *devices = (device_struct *)realloc(devices, (device_max * 2 + 1) * sizeof(device_struct));
device_max *= 2;
}
memset(&devices[i], 0, sizeof(device_struct));
devices[i].type = DEVICETYPE_CONTROLLER;
devices[i].description = strdup(device->description);
devices[i].handle_int = device->deviceID;
devices[i].buttons = device->numButtons;
devices[i].lastbutton = devices[i].buttons;
devices[i].axes = device->numAxes;
devices[i].lastaxis = devices[i].axes;
devices[i].product_id = device->productID;
devices[i].vendor_id = device->vendorID;
char name[1000];
strcpy(name, "[CONTROLLER][[NAME][");
strcat(name, devices[i].description);
strcat(name, "]]");
if (devices[i].lastbutton)
strcat(name, "[BUTTON]");
if (devices[i].lastaxis)
strcat(name, "[AXIS]");
if (devices[i].lastwheel)
strcat(name, "[WHEEL]");
devices[i].name = strdup(name);
setupDevice(&devices[i]);
device_last = i;
}
void onDeviceRemoved(struct Gamepad_device *device, void *context) {
if (verbose) {
sprintf(verboseMessage, "Device ID %u removed with context %p\n", device->deviceID, context);
std::cout << verboseMessage;
}
int i;
for (i = 1; i <= device_last; i++) {
if (devices[i].used) {
if (devices[i].type == 1) { // it's a joystick/gamepad
if (devices[i].handle_int == device->deviceID) {
char name[1000];
strcpy(name, devices[i].name);
strcat(name, "[DISCONNECTED]");
char *oldname = devices[i].name;
devices[i].name = strdup(name);
free(oldname);
devices[i].connected = 0;
}
}
}
} // i
}
static void initGamepad() {
Gamepad_deviceAttachFunc(onDeviceAttached, (void *)0x1);
Gamepad_deviceRemoveFunc(onDeviceRemoved, (void *)0x2);
Gamepad_buttonDownFunc(onButtonDown, (void *)0x3);
Gamepad_buttonUpFunc(onButtonUp, (void *)0x4);
Gamepad_axisMoveFunc(onAxisMoved, (void *)0x5);
Gamepad_init();
}
void QB64_GAMEPAD_INIT() { initGamepad(); }
void QB64_GAMEPAD_POLL() {
Gamepad_detectDevices();
Gamepad_processEvents();
}
void QB64_GAMEPAD_SHUTDOWN() {
Gamepad_deviceAttachFunc(NULL, (void *)0x1);
Gamepad_deviceRemoveFunc(NULL, (void *)0x2);
Gamepad_buttonDownFunc(NULL, (void *)0x3);
Gamepad_buttonUpFunc(NULL, (void *)0x4);
Gamepad_axisMoveFunc(NULL, (void *)0x5);
Gamepad_shutdown();
}
//----------------------------------------------------------------------------------------------------------------------
// QB64-PE Game Controller Library
// Powered by libstem Gamepad (https://github.com/ThemsAllTook/libstem_gamepad)
//----------------------------------------------------------------------------------------------------------------------
#include "game_controller.h"
#include "../../../common.h"
#include "event.h"
#include "libstem_gamepad/Gamepad.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
static bool verbose = false;
static char verboseMessage[1024];
static void onButtonDown(struct Gamepad_device *device, unsigned int buttonID, double timestamp, void *context) {
// buttonId is base 0
if (verbose) {
sprintf(verboseMessage, "Button %u down on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context);
std::cout << verboseMessage;
}
int button = buttonID;
int32_t di, controller;
controller = 0;
for (di = 1; di <= device_last; di++) {
static device_struct *d;
d = &devices[di];
if (d->used == 1) {
if (d->type == 1) {
controller++;
if (d->handle_int == device->deviceID) {
// ON STRIG event
static int32_t i;
if (controller <= 256 && button <= 255) { // within supported range
i = (controller - 1) * 256 + button;
if (onstrig[i].active) {
if (onstrig[i].id) {
if (onstrig[i].active == 1) { //(1)ON
onstrig[i].state++;
} else { //(2)STOP
onstrig[i].state = 1;
}
qbevent = 1;
}
}
} // within supported range
int32_t eventIndex = createDeviceEvent(d);
setDeviceEventButtonValue(d, eventIndex, button, 1);
commitDeviceEvent(d);
// set STRIG_button_pressed for button
if (button >= 0 && button <= 255) {
d->STRIG_button_pressed[button] = 1;
}
} // js index
} // type==1
} // used
} // di
}
static void onButtonUp(struct Gamepad_device *device, unsigned int buttonID, double timestamp, void *context) {
if (verbose) {
sprintf(verboseMessage, "Button %u up on device %u at %f with context %p\n", buttonID, device->deviceID, timestamp, context);
std::cout << verboseMessage;
}
int button = buttonID;
int32_t di;
for (di = 1; di <= device_last; di++) {
static device_struct *d;
d = &devices[di];
if (d->used == 1) {
if (d->type == 1) {
if (d->handle_int == device->deviceID) {
int32_t eventIndex = createDeviceEvent(d);
setDeviceEventButtonValue(d, eventIndex, button, 0);
commitDeviceEvent(d);
} // js index
} // type==1
} // used
} // di
}
static void onAxisMoved(struct Gamepad_device *device, unsigned int axisID, float value, float lastValue, double timestamp, void *context) {
if (verbose) {
sprintf(verboseMessage, "Axis %u moved from %f to %f on device %u at %f with context %p\n", axisID, lastValue, value, device->deviceID, timestamp,
context);
std::cout << verboseMessage;
}
int axis = axisID;
int32_t di;
for (di = 1; di <= device_last; di++) {
static device_struct *d;
d = &devices[di];
if (d->used == 1) {
if (d->type == 1) {
if (d->handle_int == device->deviceID) {
float f;
f = value;
/*
if (f==-32768) f=-32767;
f/=32767.0;
*/
if (f > 1.0)
f = 1.0;
if (f < -1.0)
f = -1.0;
int32_t eventIndex = createDeviceEvent(d);
setDeviceEventAxisValue(d, eventIndex, axis, f);
commitDeviceEvent(d);
} // js index
} // type==1
} // used
} // di
}
static void onDeviceAttached(struct Gamepad_device *device, void *context) {
if (verbose) {
sprintf(verboseMessage, "Device ID %u attached (vendor = 0x%X; product = 0x%X) with context %p\n", device->deviceID, device->vendorID,
device->productID, context);
std::cout << verboseMessage;
}
int i, x, x2;
// re-aquire a potentially dropped device in its original index
for (i = 1; i <= device_last; i++) {
if (devices[i].used) {
if (devices[i].type == 1) { // it's a joystick/gamepad
if (!devices[i].connected) {
if (device->productID == devices[i].product_id) {
if (device->vendorID == devices[i].vendor_id) {
if (device->numAxes == devices[i].axes) {
if (device->numButtons == devices[i].buttons) {
//(sometimes when gamepads are re-plugged they receieve a generic name)
// if (strlen(device->description)==strlen(devices[i].description)){//same name length
// if (strcmp(device->description,devices[i].description)==0){//same name content
// re-acquire device
devices[i].handle_int = device->deviceID;
if (!devices[i].connected) {
devices[i].connected = 1;
devices[i].name[strlen(devices[i].name) - 14] = 0; // Remove "[DISCONNECTED]"
}
devices[i].used = 1;
return;
//}
//}
}
}
}
}
}
}
}
} // i
// add new device
i = device_last + 1;
if (i > device_max) {
device_struct *devices = (device_struct *)realloc(devices, (device_max * 2 + 1) * sizeof(device_struct));
device_max *= 2;
}
memset(&devices[i], 0, sizeof(device_struct));
devices[i].type = DEVICETYPE_CONTROLLER;
devices[i].description = strdup(device->description);
devices[i].handle_int = device->deviceID;
devices[i].buttons = device->numButtons;
devices[i].lastbutton = devices[i].buttons;
devices[i].axes = device->numAxes;
devices[i].lastaxis = devices[i].axes;
devices[i].product_id = device->productID;
devices[i].vendor_id = device->vendorID;
char name[1000];
strcpy(name, "[CONTROLLER][[NAME][");
strcat(name, devices[i].description);
strcat(name, "]]");
if (devices[i].lastbutton)
strcat(name, "[BUTTON]");
if (devices[i].lastaxis)
strcat(name, "[AXIS]");
if (devices[i].lastwheel)
strcat(name, "[WHEEL]");
devices[i].name = strdup(name);
setupDevice(&devices[i]);
device_last = i;
}
static void onDeviceRemoved(struct Gamepad_device *device, void *context) {
if (verbose) {
sprintf(verboseMessage, "Device ID %u removed with context %p\n", device->deviceID, context);
std::cout << verboseMessage;
}
int i;
for (i = 1; i <= device_last; i++) {
if (devices[i].used) {
if (devices[i].type == 1) { // it's a joystick/gamepad
if (devices[i].handle_int == device->deviceID) {
char name[1000];
strcpy(name, devices[i].name);
strcat(name, "[DISCONNECTED]");
char *oldname = devices[i].name;
devices[i].name = strdup(name);
free(oldname);
devices[i].connected = 0;
}
}
}
} // i
}
void QB64_GAMEPAD_INIT() {
Gamepad_deviceAttachFunc(onDeviceAttached, (void *)0x1);
Gamepad_deviceRemoveFunc(onDeviceRemoved, (void *)0x2);
Gamepad_buttonDownFunc(onButtonDown, (void *)0x3);
Gamepad_buttonUpFunc(onButtonUp, (void *)0x4);
Gamepad_axisMoveFunc(onAxisMoved, (void *)0x5);
Gamepad_init();
}
void QB64_GAMEPAD_POLL() {
Gamepad_detectDevices();
Gamepad_processEvents();
}
void QB64_GAMEPAD_SHUTDOWN() {
Gamepad_deviceAttachFunc(NULL, (void *)0x1);
Gamepad_deviceRemoveFunc(NULL, (void *)0x2);
Gamepad_buttonDownFunc(NULL, (void *)0x3);
Gamepad_buttonUpFunc(NULL, (void *)0x4);
Gamepad_axisMoveFunc(NULL, (void *)0x5);
Gamepad_shutdown();
}

View file

@ -26,7 +26,13 @@
extern "C" {
#endif
#if defined(_MSC_VER) && (_MSC_VER <= 1600)
#define bool int
#define true 1
#define false 0
#else
#include <stdbool.h>
#endif
struct Gamepad_device {
// Unique device identifier for application session, starting at 0 for the first device attached and

View file

@ -20,12 +20,12 @@
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include "Gamepad.h"
#include "Gamepad_private.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/limits.h>
#include <linux/input.h>
#define __USE_UNIX98
#include <pthread.h>
@ -54,6 +54,7 @@ struct Gamepad_queuedEvent {
static struct Gamepad_device ** devices = NULL;
static unsigned int numDevices = 0;
static unsigned int nextDeviceID = 0;
static time_t lastInputStatTime = 0;
static pthread_mutex_t devicesMutex;
static struct Gamepad_queuedEvent * eventQueue = NULL;
@ -105,6 +106,7 @@ void Gamepad_shutdown() {
thread = ((struct Gamepad_devicePrivate *) devices[0]->privateData)->thread;
pthread_cancel(thread);
pthread_join(thread, NULL);
disposeDevice(devices[0]);
numDevices--;
for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) {
@ -118,10 +120,15 @@ void Gamepad_shutdown() {
pthread_mutex_destroy(&eventQueueMutex);
free(devices);
devices = NULL;
lastInputStatTime = 0;
for (eventIndex = 0; eventIndex < eventCount; eventIndex++) {
if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) {
disposeDevice(eventQueue[eventIndex].eventData);
} else if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_BUTTON_DOWN ||
eventQueue[eventIndex].eventType == GAMEPAD_EVENT_BUTTON_UP ||
eventQueue[eventIndex].eventType == GAMEPAD_EVENT_AXIS_MOVED) {
free(eventQueue[eventIndex].eventData);
}
}
@ -277,7 +284,6 @@ void Gamepad_detectDevices() {
char * description;
int bit;
time_t currentTime;
static time_t lastInputStatTime;
if (!inited) {
return;
@ -445,6 +451,11 @@ void Gamepad_processEvents() {
processQueuedEvent(eventQueue[eventIndex]);
if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) {
disposeDevice(eventQueue[eventIndex].eventData);
} else if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_BUTTON_DOWN ||
eventQueue[eventIndex].eventType == GAMEPAD_EVENT_BUTTON_UP ||
eventQueue[eventIndex].eventType == GAMEPAD_EVENT_AXIS_MOVED) {
free(eventQueue[eventIndex].eventData);
}
}
eventCount = 0;

View file

@ -20,8 +20,8 @@
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include "Gamepad.h"
#include "Gamepad_private.h"
#include <IOKit/hid/IOHIDLib.h>
#include <limits.h>
#include <mach/mach.h>
@ -285,7 +285,7 @@ static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHI
cfProductName = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
if (cfProductName == NULL || CFGetTypeID(cfProductName) != CFStringGetTypeID()) {
description = malloc(strlen("[Unknown]" + 1));
description = malloc(strlen("[Unknown]") + 1);
strcpy(description, "[Unknown]");
} else {

View file

@ -20,8 +20,8 @@
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include "Gamepad.h"
#include "Gamepad_private.h"
#include <stdlib.h>
void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context) = NULL;

View file

@ -34,8 +34,8 @@
#define __reserved
#endif
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include "Gamepad.h"
#include "Gamepad_private.h"
#include <stdio.h>
#include <stdlib.h>
@ -43,6 +43,118 @@
#include <regstr.h>
#include <dinput.h>
#include <XInput.h>
#include <Dbt.h>
// The following code is from SDL2
// detects gamepad device remove/attach events without having to poll multiple times per second
static bool s_bWindowsDeviceChanged = false;
static LRESULT CALLBACK PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DEVICECHANGE:
switch (wParam) {
case DBT_DEVICEARRIVAL:
if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
s_bWindowsDeviceChanged = true;
}
break;
case DBT_DEVICEREMOVECOMPLETE:
if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
s_bWindowsDeviceChanged = true;
}
break;
}
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
typedef struct {
HRESULT coinitialized;
WNDCLASSEX wincl;
HWND messageWindow;
HDEVNOTIFY hNotify;
} DeviceNotificationData;
static void CleanupDeviceNotification(DeviceNotificationData *data)
{
if (data->hNotify)
UnregisterDeviceNotification(data->hNotify);
if (data->messageWindow)
DestroyWindow(data->messageWindow);
UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance);
if (data->coinitialized == S_OK) {
CoUninitialize();
}
}
static int CreateDeviceNotification(DeviceNotificationData *data)
{
DEV_BROADCAST_DEVICEINTERFACE dbh;
GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
memset(data, 0, sizeof(*data));
data->coinitialized = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (data->coinitialized == RPC_E_CHANGED_MODE) {
data->coinitialized = CoInitializeEx(NULL, COINIT_MULTITHREADED);
}
data->wincl.hInstance = GetModuleHandle(NULL);
data->wincl.lpszClassName = "Message";
data->wincl.lpfnWndProc = PrivateJoystickDetectProc; /* This function is called by windows */
data->wincl.cbSize = sizeof (WNDCLASSEX);
if (!RegisterClassEx(&data->wincl)) {
fprintf(stderr, "Failed to create register class for joystick autodetect");
CleanupDeviceNotification(data);
return -1;
}
data->messageWindow = (HWND)CreateWindowEx(0, "Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
if (!data->messageWindow) {
fprintf(stderr, "Failed to create message window for joystick autodetect");
CleanupDeviceNotification(data);
return -1;
}
memset(&dbh, 0, sizeof(dbh));
dbh.dbcc_size = sizeof(dbh);
dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE);
if (!data->hNotify) {
fprintf(stderr, "Failed to create notify device for joystick autodetect");
CleanupDeviceNotification(data);
return -1;
}
return 0;
}
static void CheckDeviceNotification(DeviceNotificationData *data)
{
MSG msg;
if (!data->messageWindow) {
return;
}
while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) {
if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Copy from MinGW-w64 to MinGW, along with wbemcli.h, wbemprov.h, wbemdisp.h, and wbemtran.h
#ifndef __MINGW_EXTENSION
@ -94,6 +206,7 @@ struct Gamepad_devicePrivate {
unsigned int playerIndex;
};
static DeviceNotificationData notificationData;
static struct Gamepad_device ** devices = NULL;
static unsigned int numDevices = 0;
static unsigned int nextDeviceID = 0;
@ -143,10 +256,7 @@ void Gamepad_init() {
fprintf(stderr, "Gamepad_init fatal error: Couldn't load DINPUT8.dll\n");
abort();
}
DirectInput8Create_proc = (HRESULT (WINAPI *)(HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN)) GetProcAddress(module, "DirectInput8Create");
result = DirectInput8Create_proc(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (void **) &directInputInterface, NULL);
if (result != DI_OK) {
@ -154,7 +264,9 @@ void Gamepad_init() {
}
inited = true;
s_bWindowsDeviceChanged = true;
Gamepad_detectDevices();
CreateDeviceNotification(&notificationData);
}
}
@ -186,6 +298,7 @@ void Gamepad_shutdown() {
devices = NULL;
numDevices = 0;
inited = false;
CleanupDeviceNotification(&notificationData);
}
}
@ -745,6 +858,10 @@ static BOOL CALLBACK enumDevicesCallback(const DIDEVICEINSTANCE * instance, LPVO
devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1));
devices[numDevices++] = deviceRecord;
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext);
}
return DIENUM_CONTINUE;
}
@ -770,9 +887,13 @@ void Gamepad_detectDevices() {
return;
}
result = IDirectInput_EnumDevices(directInputInterface, DI8DEVCLASS_GAMECTRL, enumDevicesCallback, NULL, DIEDFL_ALLDEVICES);
if (result != DI_OK) {
fprintf(stderr, "Warning: IDirectInput_EnumDevices returned 0x%X\n", (unsigned int) result);
CheckDeviceNotification(&notificationData);
if (s_bWindowsDeviceChanged) {
result = IDirectInput_EnumDevices(directInputInterface, DI8DEVCLASS_GAMECTRL, enumDevicesCallback, NULL, DIEDFL_ALLDEVICES);
if (result != DI_OK) {
fprintf(stderr, "Warning: IDirectInput_EnumDevices returned 0x%X\n", (unsigned int) result);
}
s_bWindowsDeviceChanged = false;
}
if (xInputAvailable) {
@ -800,6 +921,9 @@ void Gamepad_detectDevices() {
devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1));
devices[numDevices++] = deviceRecord;
registeredXInputDevices[playerIndex] = deviceRecord;
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext);
}
} else if (xResult != ERROR_SUCCESS && registeredXInputDevices[playerIndex] != NULL) {
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
@ -960,7 +1084,7 @@ void Gamepad_processEvents() {
IDirectInputDevice8_Acquire(devicePrivate->deviceInterface);
result = IDirectInputDevice8_GetDeviceData(devicePrivate->deviceInterface, sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0);
}
if (result != DI_OK) {
if (!SUCCEEDED(result)) {
removeDevice(deviceIndex);
deviceIndex--;
continue;

View file

@ -1 +0,0 @@
http://forums.tigsource.com/index.php?topic=10675.80

View file

@ -1,454 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/input.h>
#define __USE_UNIX98
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
struct Gamepad_devicePrivate {
pthread_t thread;
int fd;
char * path;
int buttonMap[KEY_CNT - BTN_MISC];
int axisMap[ABS_CNT];
struct input_absinfo axisInfo[ABS_CNT];
};
struct Gamepad_queuedEvent {
unsigned int deviceID;
enum Gamepad_eventType eventType;
void * eventData;
};
static struct Gamepad_device ** devices = NULL;
static unsigned int numDevices = 0;
static unsigned int nextDeviceID = 0;
static pthread_mutex_t devicesMutex;
static struct Gamepad_queuedEvent * eventQueue = NULL;
static size_t eventQueueSize = 0;
static size_t eventCount = 0;
static pthread_mutex_t eventQueueMutex;
static bool inited = false;
#define test_bit(bitIndex, array) \
((array[(bitIndex) / (sizeof(int) * 8)] >> ((bitIndex) % (sizeof(int) * 8))) & 0x1)
void Gamepad_init() {
if (!inited) {
pthread_mutexattr_t recursiveLock;
pthread_mutexattr_init(&recursiveLock);
pthread_mutexattr_settype(&recursiveLock, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&devicesMutex, &recursiveLock);
pthread_mutex_init(&eventQueueMutex, &recursiveLock);
inited = true;
Gamepad_detectDevices();
}
}
static void disposeDevice(struct Gamepad_device * device) {
close(((struct Gamepad_devicePrivate *) device->privateData)->fd);
free(((struct Gamepad_devicePrivate *) device->privateData)->path);
free(device->privateData);
free((void *) device->description);
free(device->axisStates);
free(device->buttonStates);
free(device);
}
void Gamepad_shutdown() {
if (inited) {
unsigned int eventIndex;
unsigned int devicesLeft;
unsigned int gamepadIndex;
do {
pthread_mutex_lock(&devicesMutex);
devicesLeft = numDevices;
if (devicesLeft > 0) {
pthread_t thread;
thread = ((struct Gamepad_devicePrivate *) devices[0]->privateData)->thread;
pthread_cancel(thread);
pthread_join(thread, NULL);
numDevices--;
for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) {
devices[gamepadIndex] = devices[gamepadIndex + 1];
}
}
pthread_mutex_unlock(&devicesMutex);
} while (devicesLeft > 0);
pthread_mutex_destroy(&devicesMutex);
pthread_mutex_destroy(&eventQueueMutex);
free(devices);
devices = NULL;
for (eventIndex = 0; eventIndex < eventCount; eventIndex++) {
if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) {
disposeDevice(eventQueue[eventIndex].eventData);
}
}
eventQueueSize = 0;
eventCount = 0;
free(eventQueue);
eventQueue = NULL;
inited = false;
}
}
unsigned int Gamepad_numDevices() {
unsigned int result;
pthread_mutex_lock(&devicesMutex);
result = numDevices;
pthread_mutex_unlock(&devicesMutex);
return result;
}
struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) {
struct Gamepad_device * result;
pthread_mutex_lock(&devicesMutex);
if (deviceIndex >= numDevices) {
result = NULL;
} else {
result = devices[deviceIndex];
}
pthread_mutex_unlock(&devicesMutex);
return result;
}
static void queueEvent(unsigned int deviceID, enum Gamepad_eventType eventType, void * eventData) {
struct Gamepad_queuedEvent queuedEvent;
queuedEvent.deviceID = deviceID;
queuedEvent.eventType = eventType;
queuedEvent.eventData = eventData;
pthread_mutex_lock(&eventQueueMutex);
if (eventCount >= eventQueueSize) {
eventQueueSize = eventQueueSize == 0 ? 1 : eventQueueSize * 2;
eventQueue = realloc(eventQueue, sizeof(struct Gamepad_queuedEvent) * eventQueueSize);
}
eventQueue[eventCount++] = queuedEvent;
pthread_mutex_unlock(&eventQueueMutex);
}
static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value, float lastValue) {
struct Gamepad_axisEvent * axisEvent;
axisEvent = malloc(sizeof(struct Gamepad_axisEvent));
axisEvent->device = device;
axisEvent->timestamp = timestamp;
axisEvent->axisID = axisID;
axisEvent->value = value;
axisEvent->lastValue = lastValue;
queueEvent(device->deviceID, GAMEPAD_EVENT_AXIS_MOVED, axisEvent);
}
static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) {
struct Gamepad_buttonEvent * buttonEvent;
buttonEvent = malloc(sizeof(struct Gamepad_buttonEvent));
buttonEvent->device = device;
buttonEvent->timestamp = timestamp;
buttonEvent->buttonID = buttonID;
buttonEvent->down = down;
queueEvent(device->deviceID, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent);
}
static void * deviceThread(void * context) {
unsigned int gamepadIndex;
struct Gamepad_device * device;
struct Gamepad_devicePrivate * devicePrivate;
struct input_event event;
device = context;
devicePrivate = device->privateData;
while (read(devicePrivate->fd, &event, sizeof(struct input_event)) > 0) {
if (event.type == EV_ABS) {
float value;
if (event.code > ABS_MAX || devicePrivate->axisMap[event.code] == -1) {
continue;
}
value = (event.value - devicePrivate->axisInfo[event.code].minimum) / (float) (devicePrivate->axisInfo[event.code].maximum - devicePrivate->axisInfo[event.code].minimum) * 2.0f - 1.0f;
queueAxisEvent(device,
event.time.tv_sec + event.time.tv_usec * 0.000001,
devicePrivate->axisMap[event.code],
value,
device->axisStates[devicePrivate->axisMap[event.code]]);
device->axisStates[devicePrivate->axisMap[event.code]] = value;
} else if (event.type == EV_KEY) {
if (event.code < BTN_MISC || event.code > KEY_MAX || devicePrivate->buttonMap[event.code - BTN_MISC] == -1) {
continue;
}
queueButtonEvent(device,
event.time.tv_sec + event.time.tv_usec * 0.000001,
devicePrivate->buttonMap[event.code - BTN_MISC],
!!event.value);
device->buttonStates[devicePrivate->buttonMap[event.code - BTN_MISC]] = !!event.value;
}
}
queueEvent(device->deviceID, GAMEPAD_EVENT_DEVICE_REMOVED, device);
pthread_mutex_lock(&devicesMutex);
for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) {
if (devices[gamepadIndex] == device) {
unsigned int gamepadIndex2;
numDevices--;
for (gamepadIndex2 = gamepadIndex; gamepadIndex2 < numDevices; gamepadIndex2++) {
devices[gamepadIndex2] = devices[gamepadIndex2 + 1];
}
gamepadIndex--;
}
}
pthread_mutex_unlock(&devicesMutex);
return NULL;
}
void Gamepad_detectDevices() {
struct input_id id;
DIR * dev_input;
struct dirent * entity;
unsigned int charsConsumed;
int num;
int fd;
int evCapBits[(EV_CNT - 1) / sizeof(int) * 8 + 1];
int evKeyBits[(KEY_CNT - 1) / sizeof(int) * 8 + 1];
int evAbsBits[(ABS_CNT - 1) / sizeof(int) * 8 + 1];
char fileName[PATH_MAX];
bool duplicate;
unsigned int gamepadIndex;
struct stat statBuf;
struct Gamepad_device * deviceRecord;
struct Gamepad_devicePrivate * deviceRecordPrivate;
char name[128];
char * description;
int bit;
time_t currentTime;
static time_t lastInputStatTime;
if (!inited) {
return;
}
pthread_mutex_lock(&devicesMutex);
dev_input = opendir("/dev/input");
currentTime = time(NULL);
if (dev_input != NULL) {
while ((entity = readdir(dev_input)) != NULL) {
charsConsumed = 0;
if (sscanf(entity->d_name, "event%d%n", &num, &charsConsumed) && charsConsumed == strlen(entity->d_name)) {
snprintf(fileName, PATH_MAX, "/dev/input/%s", entity->d_name);
if (stat(fileName, &statBuf) || statBuf.st_mtime < lastInputStatTime) {
continue;
}
duplicate = false;
for (gamepadIndex = 0; gamepadIndex < numDevices; gamepadIndex++) {
if (!strcmp(((struct Gamepad_devicePrivate *) devices[gamepadIndex]->privateData)->path, fileName)) {
duplicate = true;
break;
}
}
if (duplicate) {
continue;
}
fd = open(fileName, O_RDONLY, 0);
memset(evCapBits, 0, sizeof(evCapBits));
memset(evKeyBits, 0, sizeof(evKeyBits));
memset(evAbsBits, 0, sizeof(evAbsBits));
if (ioctl(fd, EVIOCGBIT(0, sizeof(evCapBits)), evCapBits) < 0 ||
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits) < 0 ||
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits) < 0) {
close(fd);
continue;
}
if (!test_bit(EV_KEY, evCapBits) || !test_bit(EV_ABS, evCapBits) ||
!test_bit(ABS_X, evAbsBits) || !test_bit(ABS_Y, evAbsBits) ||
(!test_bit(BTN_TRIGGER, evKeyBits) && !test_bit(BTN_A, evKeyBits) && !test_bit(BTN_1, evKeyBits))) {
close(fd);
continue;
}
deviceRecord = malloc(sizeof(struct Gamepad_device));
deviceRecord->deviceID = nextDeviceID++;
devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1));
devices[numDevices++] = deviceRecord;
deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate));
deviceRecordPrivate->fd = fd;
deviceRecordPrivate->path = malloc(strlen(fileName) + 1);
strcpy(deviceRecordPrivate->path, fileName);
memset(deviceRecordPrivate->buttonMap, 0xFF, sizeof(deviceRecordPrivate->buttonMap));
memset(deviceRecordPrivate->axisMap, 0xFF, sizeof(deviceRecordPrivate->axisMap));
deviceRecord->privateData = deviceRecordPrivate;
if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) {
description = malloc(strlen(name) + 1);
strcpy(description, name);
} else {
description = malloc(strlen(fileName) + 1);
strcpy(description, fileName);
}
deviceRecord->description = description;
if (!ioctl(fd, EVIOCGID, &id)) {
deviceRecord->vendorID = id.vendor;
deviceRecord->productID = id.product;
} else {
deviceRecord->vendorID = deviceRecord->productID = 0;
}
memset(evKeyBits, 0, sizeof(evKeyBits));
memset(evAbsBits, 0, sizeof(evAbsBits));
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(evKeyBits)), evKeyBits);
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(evAbsBits)), evAbsBits);
deviceRecord->numAxes = 0;
for (bit = 0; bit < ABS_CNT; bit++) {
if (test_bit(bit, evAbsBits)) {
if (ioctl(fd, EVIOCGABS(bit), &deviceRecordPrivate->axisInfo[bit]) < 0 ||
deviceRecordPrivate->axisInfo[bit].minimum == deviceRecordPrivate->axisInfo[bit].maximum) {
continue;
}
deviceRecordPrivate->axisMap[bit] = deviceRecord->numAxes;
deviceRecord->numAxes++;
}
}
deviceRecord->numButtons = 0;
for (bit = BTN_MISC; bit < KEY_CNT; bit++) {
if (test_bit(bit, evKeyBits)) {
deviceRecordPrivate->buttonMap[bit - BTN_MISC] = deviceRecord->numButtons;
deviceRecord->numButtons++;
}
}
deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes);
deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons);
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext);
}
pthread_create(&deviceRecordPrivate->thread, NULL, deviceThread, deviceRecord);
}
}
closedir(dev_input);
}
lastInputStatTime = currentTime;
pthread_mutex_unlock(&devicesMutex);
}
static void processQueuedEvent(struct Gamepad_queuedEvent event) {
switch (event.eventType) {
case GAMEPAD_EVENT_DEVICE_ATTACHED:
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext);
}
break;
case GAMEPAD_EVENT_DEVICE_REMOVED:
if (Gamepad_deviceRemoveCallback != NULL) {
Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext);
}
break;
case GAMEPAD_EVENT_BUTTON_DOWN:
if (Gamepad_buttonDownCallback != NULL) {
struct Gamepad_buttonEvent * buttonEvent = event.eventData;
Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext);
}
break;
case GAMEPAD_EVENT_BUTTON_UP:
if (Gamepad_buttonUpCallback != NULL) {
struct Gamepad_buttonEvent * buttonEvent = event.eventData;
Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext);
}
break;
case GAMEPAD_EVENT_AXIS_MOVED:
if (Gamepad_axisMoveCallback != NULL) {
struct Gamepad_axisEvent * axisEvent = event.eventData;
Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext);
}
break;
}
}
void Gamepad_processEvents() {
unsigned int eventIndex;
static bool inProcessEvents;
if (!inited || inProcessEvents) {
return;
}
inProcessEvents = true;
pthread_mutex_lock(&eventQueueMutex);
for (eventIndex = 0; eventIndex < eventCount; eventIndex++) {
processQueuedEvent(eventQueue[eventIndex]);
if (eventQueue[eventIndex].eventType == GAMEPAD_EVENT_DEVICE_REMOVED) {
disposeDevice(eventQueue[eventIndex].eventData);
}
}
eventCount = 0;
pthread_mutex_unlock(&eventQueueMutex);
inProcessEvents = false;
}

View file

@ -1,562 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include <IOKit/hid/IOHIDLib.h>
#include <limits.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#define GAMEPAD_RUN_LOOP_MODE CFSTR("GamepadRunLoopMode")
struct HIDGamepadAxis {
IOHIDElementCookie cookie;
CFIndex logicalMin;
CFIndex logicalMax;
bool hasNullState;
bool isHatSwitch;
bool isHatSwitchSecondAxis;
};
struct HIDGamepadButton {
IOHIDElementCookie cookie;
};
struct Gamepad_devicePrivate {
IOHIDDeviceRef deviceRef;
struct HIDGamepadAxis * axisElements;
struct HIDGamepadButton * buttonElements;
};
struct Gamepad_queuedEvent {
unsigned int deviceID;
enum Gamepad_eventType eventType;
void * eventData;
};
static IOHIDManagerRef hidManager = NULL;
static struct Gamepad_device ** devices = NULL;
static unsigned int numDevices = 0;
static unsigned int nextDeviceID = 0;
static struct Gamepad_queuedEvent * inputEventQueue = NULL;
static size_t inputEventQueueSize = 0;
static size_t inputEventCount = 0;
static struct Gamepad_queuedEvent * deviceEventQueue = NULL;
static size_t deviceEventQueueSize = 0;
static size_t deviceEventCount = 0;
static void hatValueToXY(CFIndex value, CFIndex range, int * outX, int * outY) {
if (value == range) {
*outX = *outY = 0;
} else {
if (value > 0 && value < range / 2) {
*outX = 1;
} else if (value > range / 2) {
*outX = -1;
} else {
*outX = 0;
}
if (value > range / 4 * 3 || value < range / 4) {
*outY = -1;
} else if (value > range / 4 && value < range / 4 * 3) {
*outY = 1;
} else {
*outY = 0;
}
}
}
static void queueInputEvent(unsigned int deviceID, enum Gamepad_eventType eventType, void * eventData) {
struct Gamepad_queuedEvent queuedEvent;
queuedEvent.deviceID = deviceID;
queuedEvent.eventType = eventType;
queuedEvent.eventData = eventData;
if (inputEventCount >= inputEventQueueSize) {
inputEventQueueSize = inputEventQueueSize == 0 ? 1 : inputEventQueueSize * 2;
inputEventQueue = realloc(inputEventQueue, sizeof(struct Gamepad_queuedEvent) * inputEventQueueSize);
}
inputEventQueue[inputEventCount++] = queuedEvent;
}
static void queueAxisEvent(struct Gamepad_device * device, double timestamp, unsigned int axisID, float value, float lastValue) {
struct Gamepad_axisEvent * axisEvent;
axisEvent = malloc(sizeof(struct Gamepad_axisEvent));
axisEvent->device = device;
axisEvent->timestamp = timestamp;
axisEvent->axisID = axisID;
axisEvent->value = value;
axisEvent->lastValue = lastValue;
queueInputEvent(device->deviceID, GAMEPAD_EVENT_AXIS_MOVED, axisEvent);
}
static void queueButtonEvent(struct Gamepad_device * device, double timestamp, unsigned int buttonID, bool down) {
struct Gamepad_buttonEvent * buttonEvent;
buttonEvent = malloc(sizeof(struct Gamepad_buttonEvent));
buttonEvent->device = device;
buttonEvent->timestamp = timestamp;
buttonEvent->buttonID = buttonID;
buttonEvent->down = down;
queueInputEvent(device->deviceID, down ? GAMEPAD_EVENT_BUTTON_DOWN : GAMEPAD_EVENT_BUTTON_UP, buttonEvent);
}
static void onDeviceValueChanged(void * context, IOReturn result, void * sender, IOHIDValueRef value) {
struct Gamepad_device * deviceRecord;
struct Gamepad_devicePrivate * hidDeviceRecord;
IOHIDElementRef element;
IOHIDElementCookie cookie;
unsigned int axisIndex, buttonIndex;
static mach_timebase_info_data_t timebaseInfo;
if (timebaseInfo.denom == 0) {
mach_timebase_info(&timebaseInfo);
}
deviceRecord = context;
hidDeviceRecord = deviceRecord->privateData;
element = IOHIDValueGetElement(value);
cookie = IOHIDElementGetCookie(element);
for (axisIndex = 0; axisIndex < deviceRecord->numAxes; axisIndex++) {
if (!hidDeviceRecord->axisElements[axisIndex].isHatSwitchSecondAxis &&
hidDeviceRecord->axisElements[axisIndex].cookie == cookie) {
CFIndex integerValue;
if (IOHIDValueGetLength(value) > 4) {
// Workaround for a strange crash that occurs with PS3 controller; was getting lengths of 39 (!)
continue;
}
integerValue = IOHIDValueGetIntegerValue(value);
if (hidDeviceRecord->axisElements[axisIndex].isHatSwitch) {
int x, y;
// Fix for Saitek X52
if (!hidDeviceRecord->axisElements[axisIndex].hasNullState) {
if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) {
integerValue = hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1;
} else {
integerValue--;
}
}
hatValueToXY(integerValue, hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1, &x, &y);
if (x != deviceRecord->axisStates[axisIndex]) {
queueAxisEvent(deviceRecord,
IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001,
axisIndex,
x,
deviceRecord->axisStates[axisIndex]);
deviceRecord->axisStates[axisIndex] = x;
}
if (y != deviceRecord->axisStates[axisIndex + 1]) {
queueAxisEvent(deviceRecord,
IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001,
axisIndex + 1,
y,
deviceRecord->axisStates[axisIndex + 1]);
deviceRecord->axisStates[axisIndex + 1] = y;
}
} else {
float floatValue;
if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) {
hidDeviceRecord->axisElements[axisIndex].logicalMin = integerValue;
}
if (integerValue > hidDeviceRecord->axisElements[axisIndex].logicalMax) {
hidDeviceRecord->axisElements[axisIndex].logicalMax = integerValue;
}
floatValue = (integerValue - hidDeviceRecord->axisElements[axisIndex].logicalMin) / (float) (hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin) * 2.0f - 1.0f;
queueAxisEvent(deviceRecord,
IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001,
axisIndex,
floatValue,
deviceRecord->axisStates[axisIndex]);
deviceRecord->axisStates[axisIndex] = floatValue;
}
return;
}
}
for (buttonIndex = 0; buttonIndex < deviceRecord->numButtons; buttonIndex++) {
if (hidDeviceRecord->buttonElements[buttonIndex].cookie == cookie) {
bool down;
down = IOHIDValueGetIntegerValue(value);
queueButtonEvent(deviceRecord,
IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001,
buttonIndex,
down);
deviceRecord->buttonStates[buttonIndex] = down;
return;
}
}
}
static int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) {
CFTypeRef typeRef;
int value;
typeRef = IOHIDDeviceGetProperty(deviceRef, key);
if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) {
return 0;
}
CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
return value;
}
static int IOHIDDeviceGetVendorID(IOHIDDeviceRef deviceRef) {
return IOHIDDeviceGetIntProperty(deviceRef, CFSTR(kIOHIDVendorIDKey));
}
static int IOHIDDeviceGetProductID(IOHIDDeviceRef deviceRef) {
return IOHIDDeviceGetIntProperty(deviceRef, CFSTR(kIOHIDProductIDKey));
}
static void onDeviceMatched(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) {
CFArrayRef elements;
CFIndex elementIndex;
IOHIDElementRef element;
CFStringRef cfProductName;
struct Gamepad_device * deviceRecord;
struct Gamepad_devicePrivate * hidDeviceRecord;
IOHIDElementType type;
char * description;
struct Gamepad_queuedEvent queuedEvent;
deviceRecord = malloc(sizeof(struct Gamepad_device));
deviceRecord->deviceID = nextDeviceID++;
deviceRecord->vendorID = IOHIDDeviceGetVendorID(device);
deviceRecord->productID = IOHIDDeviceGetProductID(device);
deviceRecord->numAxes = 0;
deviceRecord->numButtons = 0;
devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1));
devices[numDevices++] = deviceRecord;
hidDeviceRecord = malloc(sizeof(struct Gamepad_devicePrivate));
hidDeviceRecord->deviceRef = device;
hidDeviceRecord->axisElements = NULL;
hidDeviceRecord->buttonElements = NULL;
deviceRecord->privateData = hidDeviceRecord;
cfProductName = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
if (cfProductName == NULL || CFGetTypeID(cfProductName) != CFStringGetTypeID()) {
description = malloc(strlen("[Unknown]" + 1));
strcpy(description, "[Unknown]");
} else {
CFIndex length;
CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, NULL, 100, &length);
description = malloc(length + 1);
CFStringGetBytes(cfProductName, CFRangeMake(0, CFStringGetLength(cfProductName)), kCFStringEncodingUTF8, '?', false, (UInt8 *) description, length + 1, NULL);
description[length] = '\x00';
}
deviceRecord->description = description;
elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
for (elementIndex = 0; elementIndex < CFArrayGetCount(elements); elementIndex++) {
element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, elementIndex);
type = IOHIDElementGetType(element);
// All of the axis elements I've ever detected have been kIOHIDElementTypeInput_Misc. kIOHIDElementTypeInput_Axis is only included for good faith...
if (type == kIOHIDElementTypeInput_Misc ||
type == kIOHIDElementTypeInput_Axis) {
hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1));
hidDeviceRecord->axisElements[deviceRecord->numAxes].cookie = IOHIDElementGetCookie(element);
hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMin = IOHIDElementGetLogicalMin(element);
hidDeviceRecord->axisElements[deviceRecord->numAxes].logicalMax = IOHIDElementGetLogicalMax(element);
hidDeviceRecord->axisElements[deviceRecord->numAxes].hasNullState = !!IOHIDElementHasNullState(element);
hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitch = IOHIDElementGetUsage(element) == kHIDUsage_GD_Hatswitch;
hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = false;
deviceRecord->numAxes++;
if (hidDeviceRecord->axisElements[deviceRecord->numAxes - 1].isHatSwitch) {
hidDeviceRecord->axisElements = realloc(hidDeviceRecord->axisElements, sizeof(struct HIDGamepadAxis) * (deviceRecord->numAxes + 1));
hidDeviceRecord->axisElements[deviceRecord->numAxes].isHatSwitchSecondAxis = true;
deviceRecord->numAxes++;
}
} else if (type == kIOHIDElementTypeInput_Button) {
hidDeviceRecord->buttonElements = realloc(hidDeviceRecord->buttonElements, sizeof(struct HIDGamepadButton) * (deviceRecord->numButtons + 1));
hidDeviceRecord->buttonElements[deviceRecord->numButtons].cookie = IOHIDElementGetCookie(element);
deviceRecord->numButtons++;
}
}
CFRelease(elements);
deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes);
deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons);
IOHIDDeviceRegisterInputValueCallback(device, onDeviceValueChanged, deviceRecord);
queuedEvent.deviceID = deviceRecord->deviceID;
queuedEvent.eventType = GAMEPAD_EVENT_DEVICE_ATTACHED;
queuedEvent.eventData = deviceRecord;
if (deviceEventCount >= deviceEventQueueSize) {
deviceEventQueueSize = deviceEventQueueSize == 0 ? 1 : deviceEventQueueSize * 2;
deviceEventQueue = realloc(deviceEventQueue, sizeof(struct Gamepad_queuedEvent) * deviceEventQueueSize);
}
deviceEventQueue[deviceEventCount++] = queuedEvent;
}
static void disposeDevice(struct Gamepad_device * deviceRecord) {
unsigned int inputEventIndex, deviceEventIndex;
IOHIDDeviceRegisterInputValueCallback(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->deviceRef, NULL, NULL);
for (inputEventIndex = 0; inputEventIndex < inputEventCount; inputEventIndex++) {
if (inputEventQueue[inputEventIndex].deviceID == deviceRecord->deviceID) {
unsigned int inputEventIndex2;
free(inputEventQueue[inputEventIndex].eventData);
inputEventCount--;
for (inputEventIndex2 = inputEventIndex; inputEventIndex2 < inputEventCount; inputEventIndex2++) {
inputEventQueue[inputEventIndex2] = inputEventQueue[inputEventIndex2 + 1];
}
inputEventIndex--;
}
}
for (deviceEventIndex = 0; deviceEventIndex < deviceEventCount; deviceEventIndex++) {
if (deviceEventQueue[deviceEventIndex].deviceID == deviceRecord->deviceID) {
unsigned int deviceEventIndex2;
deviceEventCount--;
for (deviceEventIndex2 = deviceEventIndex; deviceEventIndex2 < deviceEventCount; deviceEventIndex2++) {
deviceEventQueue[deviceEventIndex2] = deviceEventQueue[deviceEventIndex2 + 1];
}
deviceEventIndex--;
}
}
free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisElements);
free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->buttonElements);
free(deviceRecord->privateData);
free((void *) deviceRecord->description);
free(deviceRecord->axisStates);
free(deviceRecord->buttonStates);
free(deviceRecord);
}
static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) {
unsigned int deviceIndex;
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
if (((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->deviceRef == device) {
if (Gamepad_deviceRemoveCallback != NULL) {
Gamepad_deviceRemoveCallback(devices[deviceIndex], Gamepad_deviceRemoveContext);
}
disposeDevice(devices[deviceIndex]);
numDevices--;
for (; deviceIndex < numDevices; deviceIndex++) {
devices[deviceIndex] = devices[deviceIndex + 1];
}
return;
}
}
}
void Gamepad_init() {
if (hidManager == NULL) {
CFStringRef keys[2];
int value;
CFNumberRef values[2];
CFDictionaryRef dictionaries[3];
CFArrayRef array;
hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
keys[0] = CFSTR(kIOHIDDeviceUsagePageKey);
keys[1] = CFSTR(kIOHIDDeviceUsageKey);
value = kHIDPage_GenericDesktop;
values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
value = kHIDUsage_GD_Joystick;
values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
dictionaries[0] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
CFRelease(values[1]);
value = kHIDPage_GenericDesktop;
values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
value = kHIDUsage_GD_GamePad;
values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
dictionaries[1] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
CFRelease(values[1]);
value = kHIDPage_GenericDesktop;
values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
value = kHIDUsage_GD_MultiAxisController;
values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
dictionaries[2] = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(values[0]);
CFRelease(values[1]);
array = CFArrayCreate(kCFAllocatorDefault, (const void **) dictionaries, 3, &kCFTypeArrayCallBacks);
CFRelease(dictionaries[0]);
CFRelease(dictionaries[1]);
CFRelease(dictionaries[2]);
IOHIDManagerSetDeviceMatchingMultiple(hidManager, array);
CFRelease(array);
IOHIDManagerRegisterDeviceMatchingCallback(hidManager, onDeviceMatched, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(hidManager, onDeviceRemoved, NULL);
IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
// Force gamepads to be recognized immediately. The normal run loop mode takes a few frames,
// but we can run one iteration with a custom mode to do it without a delay.
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), GAMEPAD_RUN_LOOP_MODE);
CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true);
}
}
void Gamepad_shutdown() {
if (hidManager != NULL) {
unsigned int deviceIndex;
IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
disposeDevice(devices[deviceIndex]);
}
free(devices);
devices = NULL;
numDevices = 0;
IOHIDManagerClose(hidManager, 0);
CFRelease(hidManager);
hidManager = NULL;
}
}
unsigned int Gamepad_numDevices() {
return numDevices;
}
struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) {
if (deviceIndex >= numDevices) {
return NULL;
}
return devices[deviceIndex];
}
static void processQueuedEvent(struct Gamepad_queuedEvent event) {
switch (event.eventType) {
case GAMEPAD_EVENT_DEVICE_ATTACHED:
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext);
}
break;
case GAMEPAD_EVENT_DEVICE_REMOVED:
if (Gamepad_deviceRemoveCallback != NULL) {
Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext);
}
break;
case GAMEPAD_EVENT_BUTTON_DOWN:
if (Gamepad_buttonDownCallback != NULL) {
struct Gamepad_buttonEvent * buttonEvent = event.eventData;
Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext);
}
break;
case GAMEPAD_EVENT_BUTTON_UP:
if (Gamepad_buttonUpCallback != NULL) {
struct Gamepad_buttonEvent * buttonEvent = event.eventData;
Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext);
}
break;
case GAMEPAD_EVENT_AXIS_MOVED:
if (Gamepad_axisMoveCallback != NULL) {
struct Gamepad_axisEvent * axisEvent = event.eventData;
Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext);
}
break;
}
}
void Gamepad_detectDevices() {
unsigned int eventIndex;
if (hidManager == NULL) {
return;
}
CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true);
for (eventIndex = 0; eventIndex < deviceEventCount; eventIndex++) {
processQueuedEvent(deviceEventQueue[eventIndex]);
}
deviceEventCount = 0;
}
void Gamepad_processEvents() {
unsigned int eventIndex;
static bool inProcessEvents;
if (hidManager == NULL || inProcessEvents) {
return;
}
inProcessEvents = true;
CFRunLoopRunInMode(GAMEPAD_RUN_LOOP_MODE, 0, true);
for (eventIndex = 0; eventIndex < inputEventCount; eventIndex++) {
processQueuedEvent(inputEventQueue[eventIndex]);
free(inputEventQueue[eventIndex].eventData);
}
inputEventCount = 0;
inProcessEvents = false;
}

View file

@ -1,61 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include <stdlib.h>
void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context) = NULL;
void (* Gamepad_deviceRemoveCallback)(struct Gamepad_device * device, void * context) = NULL;
void (* Gamepad_buttonDownCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) = NULL;
void (* Gamepad_buttonUpCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) = NULL;
void (* Gamepad_axisMoveCallback)(struct Gamepad_device * device, unsigned int buttonID, float value, float lastValue, double timestamp, void * context) = NULL;
void * Gamepad_deviceAttachContext = NULL;
void * Gamepad_deviceRemoveContext = NULL;
void * Gamepad_buttonDownContext = NULL;
void * Gamepad_buttonUpContext = NULL;
void * Gamepad_axisMoveContext = NULL;
void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context) {
Gamepad_deviceAttachCallback = callback;
Gamepad_deviceAttachContext = context;
}
void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context) {
Gamepad_deviceRemoveCallback = callback;
Gamepad_deviceRemoveContext = context;
}
void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context) {
Gamepad_buttonDownCallback = callback;
Gamepad_buttonDownContext = context;
}
void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context) {
Gamepad_buttonUpCallback = callback;
Gamepad_buttonUpContext = context;
}
void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context) {
Gamepad_axisMoveCallback = callback;
Gamepad_axisMoveContext = context;
}

View file

@ -1,400 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#include "gamepad/Gamepad.h"
#include "gamepad/Gamepad_private.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <regstr.h>
struct Gamepad_devicePrivate {
UINT joystickID;
JOYINFOEX lastState;
int xAxisIndex;
int yAxisIndex;
int zAxisIndex;
int rAxisIndex;
int uAxisIndex;
int vAxisIndex;
int povXAxisIndex;
int povYAxisIndex;
UINT (* axisRanges)[2];
};
static struct Gamepad_device ** devices = NULL;
static unsigned int numDevices = 0;
static unsigned int nextDeviceID = 0;
static bool inited = false;
void Gamepad_init() {
if (!inited) {
inited = true;
Gamepad_detectDevices();
}
}
static void disposeDevice(struct Gamepad_device * deviceRecord) {
free(((struct Gamepad_devicePrivate *) deviceRecord->privateData)->axisRanges);
free(deviceRecord->privateData);
free((void *) deviceRecord->description);
free(deviceRecord->axisStates);
free(deviceRecord->buttonStates);
free(deviceRecord);
}
void Gamepad_shutdown() {
unsigned int deviceIndex;
if (inited) {
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
disposeDevice(devices[deviceIndex]);
}
free(devices);
devices = NULL;
numDevices = 0;
inited = false;
}
}
unsigned int Gamepad_numDevices() {
return numDevices;
}
struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex) {
if (deviceIndex >= numDevices) {
return NULL;
}
return devices[deviceIndex];
}
#define REG_STRING_MAX 256
static char * getDeviceDescription(UINT joystickID, JOYCAPS caps) {
char * description = NULL;
char subkey[REG_STRING_MAX];
HKEY topKey, key;
LONG result;
snprintf(subkey, REG_STRING_MAX, "%s\\%s\\%s", REGSTR_PATH_JOYCONFIG, caps.szRegKey, REGSTR_KEY_JOYCURR);
result = RegOpenKeyEx(topKey = HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &key);
if (result != ERROR_SUCCESS) {
result = RegOpenKeyEx(topKey = HKEY_CURRENT_USER, subkey, 0, KEY_READ, &key);
}
if (result == ERROR_SUCCESS) {
char value[REG_STRING_MAX];
char name[REG_STRING_MAX];
DWORD nameSize;
snprintf(value, REG_STRING_MAX, "Joystick%d%s", joystickID + 1, REGSTR_VAL_JOYOEMNAME);
nameSize = sizeof(name);
result = RegQueryValueEx(key, value, NULL, NULL, (LPBYTE) name, &nameSize);
RegCloseKey(key);
if (result == ERROR_SUCCESS) {
snprintf(subkey, REG_STRING_MAX, "%s\\%s", REGSTR_PATH_JOYOEM, name);
result = RegOpenKeyEx(topKey, subkey, 0, KEY_READ, &key);
if (result == ERROR_SUCCESS) {
nameSize = sizeof(name);
result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, NULL, &nameSize);
if (result == ERROR_SUCCESS) {
description = malloc(nameSize);
result = RegQueryValueEx(key, REGSTR_VAL_JOYOEMNAME, NULL, NULL, (LPBYTE) description, &nameSize);
}
RegCloseKey(key);
if (result == ERROR_SUCCESS) {
return description;
}
free(description);
}
}
}
description = malloc(strlen(caps.szPname) + 1);
strcpy(description, caps.szPname);
return description;
}
void Gamepad_detectDevices() {
unsigned int numPadsSupported;
unsigned int deviceIndex, deviceIndex2;
JOYINFOEX info;
JOYCAPS caps;
bool duplicate;
struct Gamepad_device * deviceRecord;
struct Gamepad_devicePrivate * deviceRecordPrivate;
UINT joystickID;
int axisIndex;
if (!inited) {
return;
}
numPadsSupported = joyGetNumDevs();
for (deviceIndex = 0; deviceIndex < numPadsSupported; deviceIndex++) {
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
joystickID = JOYSTICKID1 + deviceIndex;
if (joyGetPosEx(joystickID, &info) == JOYERR_NOERROR &&
joyGetDevCaps(joystickID, &caps, sizeof(JOYCAPS)) == JOYERR_NOERROR) {
duplicate = false;
for (deviceIndex2 = 0; deviceIndex2 < numDevices; deviceIndex2++) {
if (((struct Gamepad_devicePrivate *) devices[deviceIndex2]->privateData)->joystickID == joystickID) {
duplicate = true;
break;
}
}
if (duplicate) {
continue;
}
deviceRecord = malloc(sizeof(struct Gamepad_device));
deviceRecord->deviceID = nextDeviceID++;
deviceRecord->description = getDeviceDescription(joystickID, caps);
deviceRecord->vendorID = caps.wMid;
deviceRecord->productID = caps.wPid;
deviceRecord->numAxes = caps.wNumAxes + ((caps.wCaps & JOYCAPS_HASPOV) ? 2 : 0);
deviceRecord->numButtons = caps.wNumButtons;
deviceRecord->axisStates = calloc(sizeof(float), deviceRecord->numAxes);
deviceRecord->buttonStates = calloc(sizeof(bool), deviceRecord->numButtons);
devices = realloc(devices, sizeof(struct Gamepad_device *) * (numDevices + 1));
devices[numDevices++] = deviceRecord;
deviceRecordPrivate = malloc(sizeof(struct Gamepad_devicePrivate));
deviceRecordPrivate->joystickID = joystickID;
deviceRecordPrivate->lastState = info;
deviceRecordPrivate->xAxisIndex = 0;
deviceRecordPrivate->yAxisIndex = 1;
axisIndex = 2;
deviceRecordPrivate->zAxisIndex = (caps.wCaps & JOYCAPS_HASZ) ? axisIndex++ : -1;
deviceRecordPrivate->rAxisIndex = (caps.wCaps & JOYCAPS_HASR) ? axisIndex++ : -1;
deviceRecordPrivate->uAxisIndex = (caps.wCaps & JOYCAPS_HASU) ? axisIndex++ : -1;
deviceRecordPrivate->vAxisIndex = (caps.wCaps & JOYCAPS_HASV) ? axisIndex++ : -1;
deviceRecordPrivate->axisRanges = malloc(sizeof(UINT[2]) * axisIndex);
deviceRecordPrivate->axisRanges[0][0] = caps.wXmin;
deviceRecordPrivate->axisRanges[0][1] = caps.wXmax;
deviceRecordPrivate->axisRanges[1][0] = caps.wYmin;
deviceRecordPrivate->axisRanges[1][1] = caps.wYmax;
if (deviceRecordPrivate->zAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][0] = caps.wZmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->zAxisIndex][1] = caps.wZmax;
}
if (deviceRecordPrivate->rAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][0] = caps.wRmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->rAxisIndex][1] = caps.wRmax;
}
if (deviceRecordPrivate->uAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][0] = caps.wUmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->uAxisIndex][1] = caps.wUmax;
}
if (deviceRecordPrivate->vAxisIndex != -1) {
deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][0] = caps.wVmin;
deviceRecordPrivate->axisRanges[deviceRecordPrivate->vAxisIndex][1] = caps.wVmax;
}
deviceRecordPrivate->povXAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1;
deviceRecordPrivate->povYAxisIndex = (caps.wCaps & JOYCAPS_HASPOV) ? axisIndex++ : -1;
deviceRecord->privateData = deviceRecordPrivate;
if (Gamepad_deviceAttachCallback != NULL) {
Gamepad_deviceAttachCallback(deviceRecord, Gamepad_deviceAttachContext);
}
}
}
}
static double currentTime() {
// HACK: No timestamp data from joyGetInfoEx, so we make it up
static LARGE_INTEGER frequency;
LARGE_INTEGER currentTime;
if (frequency.QuadPart == 0) {
QueryPerformanceFrequency(&frequency);
}
QueryPerformanceCounter(&currentTime);
return (double) currentTime.QuadPart / frequency.QuadPart;
}
static void handleAxisChange(struct Gamepad_device * device, int axisIndex, DWORD ivalue) {
float value, lastValue;
struct Gamepad_devicePrivate * devicePrivate;
if (axisIndex < 0 || axisIndex >= (int) device->numAxes) {
return;
}
devicePrivate = device->privateData;
value = (ivalue - devicePrivate->axisRanges[axisIndex][0]) / (float) (devicePrivate->axisRanges[axisIndex][1] - devicePrivate->axisRanges[axisIndex][0]) * 2.0f - 1.0f;
lastValue = device->axisStates[axisIndex];
device->axisStates[axisIndex] = value;
if (Gamepad_axisMoveCallback != NULL) {
Gamepad_axisMoveCallback(device, axisIndex, value, lastValue, currentTime(), Gamepad_axisMoveContext);
}
}
static void handleButtonChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) {
bool down;
unsigned int buttonIndex;
for (buttonIndex = 0; buttonIndex < device->numButtons; buttonIndex++) {
if ((lastValue ^ value) & (1 << buttonIndex)) {
down = !!(value & (1 << buttonIndex));
device->buttonStates[buttonIndex] = down;
if (down && Gamepad_buttonDownCallback != NULL) {
Gamepad_buttonDownCallback(device, buttonIndex, currentTime(), Gamepad_buttonDownContext);
} else if (!down && Gamepad_buttonUpCallback != NULL) {
Gamepad_buttonUpCallback(device, buttonIndex, currentTime(), Gamepad_buttonUpContext);
}
}
}
}
static void povToXY(DWORD pov, int * outX, int * outY) {
if (pov == JOY_POVCENTERED) {
*outX = *outY = 0;
} else {
if (pov > JOY_POVFORWARD && pov < JOY_POVBACKWARD) {
*outX = 1;
} else if (pov > JOY_POVBACKWARD) {
*outX = -1;
} else {
*outX = 0;
}
if (pov > JOY_POVLEFT || pov < JOY_POVRIGHT) {
*outY = -1;
} else if (pov > JOY_POVRIGHT && pov < JOY_POVLEFT) {
*outY = 1;
} else {
*outY = 0;
}
}
}
static void handlePOVChange(struct Gamepad_device * device, DWORD lastValue, DWORD value) {
struct Gamepad_devicePrivate * devicePrivate;
int lastX, lastY, newX, newY;
devicePrivate = device->privateData;
if (devicePrivate->povXAxisIndex == -1 || devicePrivate->povYAxisIndex == -1) {
return;
}
povToXY(lastValue, &lastX, &lastY);
povToXY(value, &newX, &newY);
if (newX != lastX) {
device->axisStates[devicePrivate->povXAxisIndex] = newX;
if (Gamepad_axisMoveCallback != NULL) {
Gamepad_axisMoveCallback(device, devicePrivate->povXAxisIndex, newX, lastX, currentTime(), Gamepad_axisMoveContext);
}
}
if (newY != lastY) {
device->axisStates[devicePrivate->povYAxisIndex] = newY;
if (Gamepad_axisMoveCallback != NULL) {
Gamepad_axisMoveCallback(device, devicePrivate->povYAxisIndex, newY, lastY, currentTime(), Gamepad_axisMoveContext);
}
}
}
void Gamepad_processEvents() {
unsigned int deviceIndex;
static bool inProcessEvents;
JOYINFOEX info;
MMRESULT result;
struct Gamepad_device * device;
struct Gamepad_devicePrivate * devicePrivate;
if (!inited || inProcessEvents) {
return;
}
inProcessEvents = true;
for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
device = devices[deviceIndex];
devicePrivate = device->privateData;
info.dwSize = sizeof(info);
info.dwFlags = JOY_RETURNALL;
result = joyGetPosEx(devicePrivate->joystickID, &info);
if (result == JOYERR_UNPLUGGED) {
if (Gamepad_deviceRemoveCallback != NULL) {
Gamepad_deviceRemoveCallback(device, Gamepad_deviceRemoveContext);
}
disposeDevice(device);
numDevices--;
for (; deviceIndex < numDevices; deviceIndex++) {
devices[deviceIndex] = devices[deviceIndex + 1];
}
} else if (result == JOYERR_NOERROR) {
if (info.dwXpos != devicePrivate->lastState.dwXpos) {
handleAxisChange(device, devicePrivate->xAxisIndex, info.dwXpos);
}
if (info.dwYpos != devicePrivate->lastState.dwYpos) {
handleAxisChange(device, devicePrivate->yAxisIndex, info.dwYpos);
}
if (info.dwZpos != devicePrivate->lastState.dwZpos) {
handleAxisChange(device, devicePrivate->zAxisIndex, info.dwZpos);
}
if (info.dwRpos != devicePrivate->lastState.dwRpos) {
handleAxisChange(device, devicePrivate->rAxisIndex, info.dwRpos);
}
if (info.dwUpos != devicePrivate->lastState.dwUpos) {
handleAxisChange(device, devicePrivate->uAxisIndex, info.dwUpos);
}
if (info.dwVpos != devicePrivate->lastState.dwVpos) {
handleAxisChange(device, devicePrivate->vAxisIndex, info.dwVpos);
}
if (info.dwPOV != devicePrivate->lastState.dwPOV) {
handlePOVChange(device, devicePrivate->lastState.dwPOV, info.dwPOV);
}
if (info.dwButtons != devicePrivate->lastState.dwButtons) {
handleButtonChange(device, devicePrivate->lastState.dwButtons, info.dwButtons);
}
devicePrivate->lastState = info;
}
}
inProcessEvents = false;
}

View file

@ -1,128 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#ifndef __GAMEPAD_H__
#define __GAMEPAD_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
struct Gamepad_device {
// Unique device identifier for application session, starting at 0 for the first device attached and
// incrementing by 1 for each additional device. If a device is removed and subsequently reattached
// during the same application session, it will have a new deviceID.
unsigned int deviceID;
// Human-readable device name
const char * description;
// USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented.
int vendorID;
int productID;
// Number of axis elements belonging to the device
unsigned int numAxes;
// Number of button elements belonging to the device
unsigned int numButtons;
// Array[numAxes] of values representing the current state of each axis, in the range [-1..1]
float * axisStates;
// Array[numButtons] of values representing the current state of each button
bool * buttonStates;
// Platform-specific device data storage. Don't touch unless you know what you're doing and don't
// mind your code breaking in future versions of this library.
void * privateData;
};
/* Initializes gamepad library and detects initial devices. Call this before any other Gamepad_*()
function, other than callback registration functions. If you want to receive deviceAttachFunc
callbacks from devices detected in Gamepad_init(), you must call Gamepad_deviceAttachFunc()
before calling Gamepad_init().
This function must be called from the same thread that will be calling Gamepad_processEvents()
and Gamepad_detectDevices(). */
void Gamepad_init();
/* Tears down all data structures created by the gamepad library and releases any memory that was
allocated. It is not necessary to call this function at application termination, but it's
provided in case you want to free memory associated with gamepads at some earlier time. */
void Gamepad_shutdown();
/* Returns the number of currently attached gamepad devices. */
unsigned int Gamepad_numDevices();
/* Returns the specified Gamepad_device struct, or NULL if deviceIndex is out of bounds. */
struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex);
/* Polls for any devices that have been attached since the last call to Gamepad_detectDevices() or
Gamepad_init(). If any new devices are found, the callback registered with
Gamepad_deviceAttachFunc() (if any) will be called once per newly detected device.
Note that depending on implementation, you may receive button and axis event callbacks for
devices that have not yet been detected with Gamepad_detectDevices(). You can safely ignore
these events, but be aware that your callbacks might receive a device ID that hasn't been seen
by your deviceAttachFunc. */
void Gamepad_detectDevices();
/* Reads pending input from all attached devices and calls the appropriate input callbacks, if any
have been registered. */
void Gamepad_processEvents();
/* Registers a function to be called whenever a device is attached. The specified function will be
called only during calls to Gamepad_init() and Gamepad_detectDevices(), in the thread from
which those functions were called. Calling this function with a NULL argument will stop any
previously registered callback from being called subsequently. */
void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context);
/* Registers a function to be called whenever a device is detached. The specified function can be
called at any time, and will not necessarily be called from the main thread. Calling this
function with a NULL argument will stop any previously registered callback from being called
subsequently. */
void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context);
/* Registers a function to be called whenever a button on any attached device is pressed. The
specified function will be called only during calls to Gamepad_processEvents(), in the
thread from which Gamepad_processEvents() was called. Calling this function with a NULL
argument will stop any previously registered callback from being called subsequently. */
void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context);
/* Registers a function to be called whenever a button on any attached device is released. The
specified function will be called only during calls to Gamepad_processEvents(), in the
thread from which Gamepad_processEvents() was called. Calling this function with a NULL
argument will stop any previously registered callback from being called subsequently. */
void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context);
/* Registers a function to be called whenever an axis on any attached device is moved. The
specified function will be called only during calls to Gamepad_processEvents(), in the
thread from which Gamepad_processEvents() was called. Calling this function with a NULL
argument will stop any previously registered callback from being called subsequently. */
void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,76 +0,0 @@
/*
Copyright (c) 2014 Alex Diener
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.
Alex Diener alex@ludobloom.com
*/
#ifndef __GAMEPAD_PRIVATE_H__
#define __GAMEPAD_PRIVATE_H__
enum Gamepad_eventType {
GAMEPAD_EVENT_DEVICE_ATTACHED,
GAMEPAD_EVENT_DEVICE_REMOVED,
GAMEPAD_EVENT_BUTTON_DOWN,
GAMEPAD_EVENT_BUTTON_UP,
GAMEPAD_EVENT_AXIS_MOVED
};
struct Gamepad_buttonEvent {
// Device that generated the event
struct Gamepad_device * device;
// Relative time of the event, in seconds
double timestamp;
// Button being pushed or released
unsigned int buttonID;
// True if button is down
bool down;
};
struct Gamepad_axisEvent {
// Device that generated the event
struct Gamepad_device * device;
// Relative time of the event, in seconds
double timestamp;
// Axis being moved
unsigned int axisID;
// Axis position value, in the range [-1..1]
float value;
// Previous axis position value, in the range [-1..1]
float lastValue;
};
extern void (* Gamepad_deviceAttachCallback)(struct Gamepad_device * device, void * context);
extern void (* Gamepad_deviceRemoveCallback)(struct Gamepad_device * device, void * context);
extern void (* Gamepad_buttonDownCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context);
extern void (* Gamepad_buttonUpCallback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context);
extern void (* Gamepad_axisMoveCallback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context);
extern void * Gamepad_deviceAttachContext;
extern void * Gamepad_deviceRemoveContext;
extern void * Gamepad_buttonDownContext;
extern void * Gamepad_buttonUpContext;
extern void * Gamepad_axisMoveContext;
#endif

View file

@ -1,3 +1,6 @@
--------------------------------------------------------------------------------
License of libstem Gamepad:
Copyright (c) 2014 Alex Diener
This software is provided 'as-is', without any express or implied
@ -16,4 +19,4 @@ freely, subject to the following restrictions:
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Alex Diener alex@ludobloom.com
Alex Diener alex@ludobloom.com