diff --git a/CMakeLists.txt b/CMakeLists.txt index a1f8c9d26..0f8c50fa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ option(GLFW_DOCUMENT_INTERNALS "Include internals in documentation" OFF) if (WIN32) option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF) + option(GLFW_USE_XINPUT "Use XInput to handle josticks" ON) endif() if (APPLE) @@ -210,6 +211,10 @@ if (_GLFW_WIN32) if (GLFW_USE_HYBRID_HPG) set(_GLFW_USE_HYBRID_HPG 1) endif() + + if (GLFW_USE_XINPUT) + set(_GLFW_USE_XINPUT 1) + endif() endif() #-------------------------------------------------------------------- @@ -403,7 +408,7 @@ configure_package_config_file("${GLFW_SOURCE_DIR}/src/glfw3Config.cmake.in" PATH_VARS CMAKE_INSTALL_PREFIX NO_CHECK_REQUIRED_COMPONENTS_MACRO) -write_basic_package_version_file("${GLFW_BINARY_DIR}/src/glfw3ConfigVersion.cmake" +write_basic_package_version_file("${GLFW_BINARY_DIR}/src/glfw3ConfigVersion.cmake" VERSION ${GLFW_VERSION_FULL} COMPATIBILITY SameMajorVersion) @@ -461,4 +466,3 @@ if (GLFW_INSTALL) "${GLFW_BINARY_DIR}/cmake_uninstall.cmake") endif() endif() - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 77a8501f6..d0ee12039 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,10 +17,16 @@ if (_GLFW_COCOA) set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_monitor.m cocoa_window.m iokit_joystick.m mach_time.c posix_tls.c) elseif (_GLFW_WIN32) - set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_tls.h - winmm_joystick.h) + set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_tls.h) set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_monitor.c win32_time.c - win32_tls.c win32_window.c winmm_joystick.c) + win32_tls.c win32_window.c) + if (_GLFW_USE_XINPUT) + list(APPEND glfw_HEADERS xinput_joystick.h) + list(APPEND glfw_SOURCES xinput_joystick.c) + else() + list(APPEND glfw_HEADERS winmm_joystick.h) + list(APPEND glfw_SOURCES winmm_joystick.c) + endif() elseif (_GLFW_X11) set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h linux_joystick.h posix_time.h posix_tls.h) @@ -93,4 +99,3 @@ endif() if (GLFW_INSTALL) install(TARGETS glfw EXPORT glfwTargets DESTINATION lib${LIB_SUFFIX}) endif() - diff --git a/src/glfw_config.h.in b/src/glfw_config.h.in index 6de975091..2916a0a38 100644 --- a/src/glfw_config.h.in +++ b/src/glfw_config.h.in @@ -60,6 +60,9 @@ // Define this to 1 to force use of high-performance GPU on hybrid systems #cmakedefine _GLFW_USE_HYBRID_HPG +// Define this to 1 to use XInput to handle joysticks +#cmakedefine _GLFW_USE_XINPUT + // Define this to 1 if the XInput X11 extension is available #cmakedefine _GLFW_HAS_XINPUT // Define this to 1 if the Xxf86vm X11 extension is available @@ -71,4 +74,3 @@ #cmakedefine _GLFW_USE_MENUBAR // Define this to 1 if windows should use full resolution on Retina displays #cmakedefine _GLFW_USE_RETINA - diff --git a/src/win32_init.c b/src/win32_init.c index cd749b40a..4c0d4d9f9 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -69,15 +69,32 @@ static GLFWbool initLibraries(void) return GLFW_FALSE; } +#ifndef _GLFW_USE_XINPUT _glfw.win32.winmm.joyGetDevCaps = (JOYGETDEVCAPS_T) GetProcAddress(_glfw.win32.winmm.instance, "joyGetDevCapsW"); _glfw.win32.winmm.joyGetPos = (JOYGETPOS_T) GetProcAddress(_glfw.win32.winmm.instance, "joyGetPos"); _glfw.win32.winmm.joyGetPosEx = (JOYGETPOSEX_T) GetProcAddress(_glfw.win32.winmm.instance, "joyGetPosEx"); +#endif _glfw.win32.winmm.timeGetTime = (TIMEGETTIME_T) GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); +#ifdef _GLFW_USE_XINPUT + _glfw.win32.xinput.instance = LoadLibraryW(L"XInput9_1_0.dll"); + if (!_glfw.win32.xinput.instance) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to load XInput9_1_0.dll"); + return GLFW_FALSE; + } + + _glfw.win32.xinput.XInputGetCapabilities = (XINPUTGETCAPABILITIES_T) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); + _glfw.win32.xinput.XInputGetState = (XINPUTGETSTATE_T) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); +#endif + _glfw.win32.user32.instance = LoadLibraryW(L"user32.dll"); if (!_glfw.win32.user32.instance) { @@ -380,4 +397,3 @@ const char* _glfwPlatformGetVersionString(void) #endif ; } - diff --git a/src/win32_platform.h b/src/win32_platform.h index 9be21c8d2..892a25ee9 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -64,6 +64,9 @@ #include #include #include +#ifdef _GLFW_USE_XINPUT + #include +#endif #if defined(_MSC_VER) #include @@ -100,14 +103,18 @@ typedef struct tagCHANGEFILTERSTRUCT #endif /*Windows 7*/ // winmm.dll function pointer typedefs -typedef MMRESULT (WINAPI * JOYGETDEVCAPS_T)(UINT,LPJOYCAPS,UINT); -typedef MMRESULT (WINAPI * JOYGETPOS_T)(UINT,LPJOYINFO); -typedef MMRESULT (WINAPI * JOYGETPOSEX_T)(UINT,LPJOYINFOEX); typedef DWORD (WINAPI * TIMEGETTIME_T)(void); -#define _glfw_joyGetDevCaps _glfw.win32.winmm.joyGetDevCaps -#define _glfw_joyGetPos _glfw.win32.winmm.joyGetPos -#define _glfw_joyGetPosEx _glfw.win32.winmm.joyGetPosEx +#ifndef _GLFW_USE_XINPUT +typedef MMRESULT(WINAPI * JOYGETDEVCAPS_T)(UINT,LPJOYCAPS,UINT); +typedef MMRESULT(WINAPI * JOYGETPOS_T)(UINT,LPJOYINFO); +typedef MMRESULT(WINAPI * JOYGETPOSEX_T)(UINT,LPJOYINFOEX); +#endif #define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime +#ifndef _GLFW_USE_XINPUT + #define _glfw_joyGetDevCaps _glfw.win32.winmm.joyGetDevCaps + #define _glfw_joyGetPos _glfw.win32.winmm.joyGetPos + #define _glfw_joyGetPosEx _glfw.win32.winmm.joyGetPosEx +#endif // user32.dll function pointer typedefs typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void); @@ -121,12 +128,24 @@ typedef HRESULT (WINAPI * DWMFLUSH_T)(VOID); #define _glfw_DwmIsCompositionEnabled _glfw.win32.dwmapi.DwmIsCompositionEnabled #define _glfw_DwmFlush _glfw.win32.dwmapi.DwmFlush +#ifdef _GLFW_USE_XINPUT +// Xinput.dll function pointer typedefs +typedef DWORD(WINAPI * XINPUTGETCAPABILITIES_T)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD(WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*); +#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities +#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState +#endif + #define _GLFW_RECREATION_NOT_NEEDED 0 #define _GLFW_RECREATION_REQUIRED 1 #define _GLFW_RECREATION_IMPOSSIBLE 2 #include "win32_tls.h" -#include "winmm_joystick.h" +#ifdef _GLFW_USE_XINPUT + #include "xinput_joystick.h" +#else + #include "winmm_joystick.h" +#endif #if defined(_GLFW_WGL) #include "wgl_context.h" @@ -171,9 +190,11 @@ typedef struct _GLFWlibraryWin32 // winmm.dll struct { HINSTANCE instance; +#ifndef _GLFW_USE_XINPUT JOYGETDEVCAPS_T joyGetDevCaps; JOYGETPOS_T joyGetPos; JOYGETPOSEX_T joyGetPosEx; +#endif TIMEGETTIME_T timeGetTime; } winmm; @@ -191,6 +212,14 @@ typedef struct _GLFWlibraryWin32 DWMFLUSH_T DwmFlush; } dwmapi; +#ifdef _GLFW_USE_XINPUT + // Xinput.dll + struct { + HINSTANCE instance; + XINPUTGETCAPABILITIES_T XInputGetCapabilities; + XINPUTGETSTATE_T XInputGetState; + } xinput; +#endif } _GLFWlibraryWin32; diff --git a/src/xinput_joystick.c b/src/xinput_joystick.c new file mode 100644 index 000000000..76769da1f --- /dev/null +++ b/src/xinput_joystick.c @@ -0,0 +1,147 @@ +//======================================================================== +// GLFW 3.1 XInput - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2015 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +static const DWORD ORDERED_XINPUT_BUTTONS[XINPUT_BUTTONS_COUNT] = { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, +}; + +static float normalizeThumb(SHORT pos, SHORT deadzone) +{ + const SHORT THUMB_MIN = -32768; + const SHORT THUMB_MAX = 32767; + + if (abs(pos) < deadzone) + return 0.f; + + return 2.f * ((float)(pos - THUMB_MIN) / (float)(THUMB_MAX - THUMB_MIN)) - 1.f; +} + +static float normalizeTrigger(BYTE pos) +{ + return (float)pos / 255.f; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +void _glfwInitJoysticks(void) +{ +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticks(void) +{ + int i; + + for (i = 0; i < GLFW_JOYSTICK_LAST; i++) + free(_glfw.xinput_js[i].name); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformJoystickPresent(int joy) +{ + XINPUT_CAPABILITIES capabilities; + return _glfw_XInputGetCapabilities(joy, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS; +} + +const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +{ + XINPUT_STATE state; + XINPUT_GAMEPAD gamepad; + float* axes = _glfw.xinput_js[joy].axes; + + ZeroMemory(&state, sizeof(XINPUT_STATE)); + if (_glfw_XInputGetState(joy, &state) != ERROR_SUCCESS) + return NULL; + + gamepad = state.Gamepad; + + axes[(*count)++] = normalizeTrigger(gamepad.bLeftTrigger); + axes[(*count)++] = normalizeTrigger(gamepad.bRightTrigger); + axes[(*count)++] = normalizeThumb(gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + axes[(*count)++] = normalizeThumb(gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + axes[(*count)++] = normalizeThumb(gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); + axes[(*count)++] = normalizeThumb(gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); + + return axes; +} + +const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) +{ + XINPUT_STATE state; + unsigned char* buttons = _glfw.xinput_js[joy].buttons; + + ZeroMemory(&state, sizeof(XINPUT_STATE)); + if (_glfw_XInputGetState(joy, &state) != ERROR_SUCCESS) + return NULL; + + *count = XINPUT_BUTTONS_COUNT; + for (int i = 0; i < XINPUT_BUTTONS_COUNT; i++) + buttons[i] = (unsigned char)((state.Gamepad.wButtons & ORDERED_XINPUT_BUTTONS[i]) ? GLFW_PRESS : GLFW_RELEASE); + + return buttons; +} + +const char* _glfwPlatformGetJoystickName(int joy) +{ + char name[64]; + XINPUT_STATE state; + ZeroMemory(&state, sizeof(XINPUT_STATE)); + + if (!_glfwPlatformJoystickPresent(joy)) + return NULL; + + sprintf_s(name, sizeof(name), "XInput Joystick %d", joy + 1); + free(_glfw.xinput_js[joy].name); + _glfw.xinput_js[joy].name = strdup(name); + return _glfw.xinput_js[joy].name; +} diff --git a/src/xinput_joystick.h b/src/xinput_joystick.h new file mode 100644 index 000000000..d3a422245 --- /dev/null +++ b/src/xinput_joystick.h @@ -0,0 +1,48 @@ +//======================================================================== +// GLFW 3.2 XInput - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2015 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_xinput_joystick_h_ +#define _glfw3_xinput_joystick_h_ + +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ + _GLFWjoystickXInput xinput_js[GLFW_JOYSTICK_LAST + 1] + + +// Windows-specific per-joystick data +// +#define XINPUT_BUTTONS_COUNT 14 +typedef struct _GLFWjoystickXInput +{ + float axes[6]; + unsigned char buttons[XINPUT_BUTTONS_COUNT]; + char* name; +} _GLFWjoystickXInput; + + +void _glfwInitJoysticks(void); +void _glfwTerminateJoysticks(void); + +#endif // _glfw3_xinput_joystick_h_