mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-28 15:55:39 +00:00
update sdl to https://github.com/libsdl-org/SDL 22March 2022
This commit is contained in:
parent
ee4253c982
commit
2614274639
1225 changed files with 148950 additions and 51674 deletions
File diff suppressed because it is too large
Load diff
35
Engine/lib/sdl/src/hidapi/SDL_hidapi_c.h
Normal file
35
Engine/lib/sdl/src/hidapi/SDL_hidapi_c.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#ifdef SDL_LIBUSB_DYNAMIC
|
||||
#define HAVE_ENABLE_GAMECUBE_ADAPTORS
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
|
||||
extern void SDL_EnableGameCubeAdaptors(void);
|
||||
#endif
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
||||
/* vi: set sts=4 ts=4 sw=4 expandtab: */
|
||||
|
|
@ -1,17 +1,33 @@
|
|||
//=================== Copyright Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2021 Valve Corporation
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
// Purpose: A wrapper implementing "HID" API for Android
|
||||
//
|
||||
// This layer glues the hidapi API to Android's USB and BLE stack.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
// Common to stub version and non-stub version of functions
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h> // For ETIMEDOUT and ECONNRESET
|
||||
#include <stdlib.h> // For malloc() and free()
|
||||
#include <string.h> // For memcpy()
|
||||
|
||||
#define TAG "hidapi"
|
||||
|
||||
|
|
@ -31,6 +47,35 @@
|
|||
#define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
|
||||
#define HID_DEVICE_MANAGER_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, HIDDeviceManager, function)
|
||||
|
||||
|
||||
#if !SDL_HIDAPI_DISABLED
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "../../core/android/SDL_android.h"
|
||||
|
||||
#define hid_init PLATFORM_hid_init
|
||||
#define hid_exit PLATFORM_hid_exit
|
||||
#define hid_enumerate PLATFORM_hid_enumerate
|
||||
#define hid_free_enumeration PLATFORM_hid_free_enumeration
|
||||
#define hid_open PLATFORM_hid_open
|
||||
#define hid_open_path PLATFORM_hid_open_path
|
||||
#define hid_write PLATFORM_hid_write
|
||||
#define hid_read_timeout PLATFORM_hid_read_timeout
|
||||
#define hid_read PLATFORM_hid_read
|
||||
#define hid_set_nonblocking PLATFORM_hid_set_nonblocking
|
||||
#define hid_send_feature_report PLATFORM_hid_send_feature_report
|
||||
#define hid_get_feature_report PLATFORM_hid_get_feature_report
|
||||
#define hid_close PLATFORM_hid_close
|
||||
#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string
|
||||
#define hid_get_product_string PLATFORM_hid_get_product_string
|
||||
#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string
|
||||
#define hid_get_indexed_string PLATFORM_hid_get_indexed_string
|
||||
#define hid_error PLATFORM_hid_error
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h> // For ETIMEDOUT and ECONNRESET
|
||||
#include <stdlib.h> // For malloc() and free()
|
||||
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
||||
typedef uint32_t uint32;
|
||||
|
|
@ -154,7 +199,7 @@ public:
|
|||
}
|
||||
|
||||
m_nSize = nSize;
|
||||
memcpy( m_pData, pData, nSize );
|
||||
SDL_memcpy( m_pData, pData, nSize );
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
|
@ -269,9 +314,9 @@ private:
|
|||
|
||||
static jbyteArray NewByteArray( JNIEnv* env, const uint8_t *pData, size_t nDataLen )
|
||||
{
|
||||
jbyteArray array = env->NewByteArray( nDataLen );
|
||||
jbyteArray array = env->NewByteArray( (jsize)nDataLen );
|
||||
jbyte *pBuf = env->GetByteArrayElements( array, NULL );
|
||||
memcpy( pBuf, pData, nDataLen );
|
||||
SDL_memcpy( pBuf, pData, nDataLen );
|
||||
env->ReleaseByteArrayElements( array, pBuf, 0 );
|
||||
|
||||
return array;
|
||||
|
|
@ -282,7 +327,7 @@ static char *CreateStringFromJString( JNIEnv *env, const jstring &sString )
|
|||
size_t nLength = env->GetStringUTFLength( sString );
|
||||
const char *pjChars = env->GetStringUTFChars( sString, NULL );
|
||||
char *psString = (char*)malloc( nLength + 1 );
|
||||
memcpy( psString, pjChars, nLength );
|
||||
SDL_memcpy( psString, pjChars, nLength );
|
||||
psString[ nLength ] = '\0';
|
||||
env->ReleaseStringUTFChars( sString, pjChars );
|
||||
return psString;
|
||||
|
|
@ -305,9 +350,9 @@ static wchar_t *CreateWStringFromJString( JNIEnv *env, const jstring &sString )
|
|||
|
||||
static wchar_t *CreateWStringFromWString( const wchar_t *pwSrc )
|
||||
{
|
||||
size_t nLength = wcslen( pwSrc );
|
||||
size_t nLength = SDL_wcslen( pwSrc );
|
||||
wchar_t *pwString = (wchar_t*)malloc( ( nLength + 1 ) * sizeof( wchar_t ) );
|
||||
memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
|
||||
SDL_memcpy( pwString, pwSrc, nLength * sizeof( wchar_t ) );
|
||||
pwString[ nLength ] = '\0';
|
||||
return pwString;
|
||||
}
|
||||
|
|
@ -316,7 +361,7 @@ static hid_device_info *CopyHIDDeviceInfo( const hid_device_info *pInfo )
|
|||
{
|
||||
hid_device_info *pCopy = new hid_device_info;
|
||||
*pCopy = *pInfo;
|
||||
pCopy->path = strdup( pInfo->path );
|
||||
pCopy->path = SDL_strdup( pInfo->path );
|
||||
pCopy->product_string = CreateWStringFromWString( pInfo->product_string );
|
||||
pCopy->manufacturer_string = CreateWStringFromWString( pInfo->manufacturer_string );
|
||||
pCopy->serial_number = CreateWStringFromWString( pInfo->serial_number );
|
||||
|
|
@ -334,17 +379,49 @@ static void FreeHIDDeviceInfo( hid_device_info *pInfo )
|
|||
|
||||
static jclass g_HIDDeviceManagerCallbackClass;
|
||||
static jobject g_HIDDeviceManagerCallbackHandler;
|
||||
static jmethodID g_midHIDDeviceManagerInitialize;
|
||||
static jmethodID g_midHIDDeviceManagerOpen;
|
||||
static jmethodID g_midHIDDeviceManagerSendOutputReport;
|
||||
static jmethodID g_midHIDDeviceManagerSendFeatureReport;
|
||||
static jmethodID g_midHIDDeviceManagerGetFeatureReport;
|
||||
static jmethodID g_midHIDDeviceManagerClose;
|
||||
static bool g_initialized = false;
|
||||
|
||||
static uint64_t get_timespec_ms( const struct timespec &ts )
|
||||
{
|
||||
return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
static void ExceptionCheck( JNIEnv *env, const char *pszClassName, const char *pszMethodName )
|
||||
{
|
||||
if ( env->ExceptionCheck() )
|
||||
{
|
||||
// Get our exception
|
||||
jthrowable jExcept = env->ExceptionOccurred();
|
||||
|
||||
// Clear the exception so we can call JNI again
|
||||
env->ExceptionClear();
|
||||
|
||||
// Get our exception message
|
||||
jclass jExceptClass = env->GetObjectClass( jExcept );
|
||||
jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
|
||||
jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
|
||||
const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
|
||||
|
||||
// ...and log it.
|
||||
LOGE( "%s%s%s threw an exception: %s",
|
||||
pszClassName ? pszClassName : "",
|
||||
pszClassName ? "::" : "",
|
||||
pszMethodName, pszMessage );
|
||||
|
||||
// Cleanup
|
||||
env->ReleaseStringUTFChars( jMessage, pszMessage );
|
||||
env->DeleteLocalRef( jMessage );
|
||||
env->DeleteLocalRef( jExceptClass );
|
||||
env->DeleteLocalRef( jExcept );
|
||||
}
|
||||
}
|
||||
|
||||
class CHIDDevice
|
||||
{
|
||||
public:
|
||||
|
|
@ -404,29 +481,7 @@ public:
|
|||
|
||||
void ExceptionCheck( JNIEnv *env, const char *pszMethodName )
|
||||
{
|
||||
if ( env->ExceptionCheck() )
|
||||
{
|
||||
// Get our exception
|
||||
jthrowable jExcept = env->ExceptionOccurred();
|
||||
|
||||
// Clear the exception so we can call JNI again
|
||||
env->ExceptionClear();
|
||||
|
||||
// Get our exception message
|
||||
jclass jExceptClass = env->GetObjectClass( jExcept );
|
||||
jmethodID jMessageMethod = env->GetMethodID( jExceptClass, "getMessage", "()Ljava/lang/String;" );
|
||||
jstring jMessage = (jstring)( env->CallObjectMethod( jExcept, jMessageMethod ) );
|
||||
const char *pszMessage = env->GetStringUTFChars( jMessage, NULL );
|
||||
|
||||
// ...and log it.
|
||||
LOGE( "CHIDDevice::%s threw an exception: %s", pszMethodName, pszMessage );
|
||||
|
||||
// Cleanup
|
||||
env->ReleaseStringUTFChars( jMessage, pszMessage );
|
||||
env->DeleteLocalRef( jMessage );
|
||||
env->DeleteLocalRef( jExceptClass );
|
||||
env->DeleteLocalRef( jExcept );
|
||||
}
|
||||
::ExceptionCheck( env, "CHIDDevice", pszMethodName );
|
||||
}
|
||||
|
||||
bool BOpen()
|
||||
|
|
@ -527,12 +582,12 @@ public:
|
|||
if ( m_bIsBLESteamController )
|
||||
{
|
||||
data[0] = 0x03;
|
||||
memcpy( data + 1, buffer.data(), nDataLen );
|
||||
SDL_memcpy( data + 1, buffer.data(), nDataLen );
|
||||
++nDataLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data, buffer.data(), nDataLen );
|
||||
SDL_memcpy( data, buffer.data(), nDataLen );
|
||||
}
|
||||
m_vecData.pop_front();
|
||||
|
||||
|
|
@ -541,7 +596,7 @@ public:
|
|||
// data[0], data[1], data[2], data[3],
|
||||
// data[4], data[5], data[6], data[7]);
|
||||
|
||||
return nDataLen;
|
||||
return (int)nDataLen;
|
||||
}
|
||||
|
||||
int SendOutputReport( const unsigned char *pData, size_t nDataLen )
|
||||
|
|
@ -669,11 +724,11 @@ public:
|
|||
}
|
||||
|
||||
size_t uBytesToCopy = m_featureReport.size() > nDataLen ? nDataLen : m_featureReport.size();
|
||||
memcpy( pData, m_featureReport.data(), uBytesToCopy );
|
||||
SDL_memcpy( pData, m_featureReport.data(), uBytesToCopy );
|
||||
m_featureReport.clear();
|
||||
LOGV( "=== Got %u bytes", uBytesToCopy );
|
||||
|
||||
return uBytesToCopy;
|
||||
return (int)uBytesToCopy;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +870,11 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallba
|
|||
if ( objClass )
|
||||
{
|
||||
g_HIDDeviceManagerCallbackClass = reinterpret_cast< jclass >( env->NewGlobalRef( objClass ) );
|
||||
g_midHIDDeviceManagerInitialize = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "initialize", "(ZZ)Z" );
|
||||
if ( !g_midHIDDeviceManagerInitialize )
|
||||
{
|
||||
__android_log_print(ANDROID_LOG_ERROR, TAG, "HIDDeviceRegisterCallback: callback class missing initialize" );
|
||||
}
|
||||
g_midHIDDeviceManagerOpen = env->GetMethodID( g_HIDDeviceManagerCallbackClass, "openDevice", "(I)Z" );
|
||||
if ( !g_midHIDDeviceManagerOpen )
|
||||
{
|
||||
|
|
@ -854,6 +914,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallbac
|
|||
g_HIDDeviceManagerCallbackClass = NULL;
|
||||
env->DeleteGlobalRef( g_HIDDeviceManagerCallbackHandler );
|
||||
g_HIDDeviceManagerCallbackHandler = NULL;
|
||||
g_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -863,7 +924,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNI
|
|||
LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface );
|
||||
|
||||
hid_device_info *pInfo = new hid_device_info;
|
||||
memset( pInfo, 0, sizeof( *pInfo ) );
|
||||
SDL_memset( pInfo, 0, sizeof( *pInfo ) );
|
||||
pInfo->path = CreateStringFromJString( env, sIdentifier );
|
||||
pInfo->vendor_id = nVendorId;
|
||||
pInfo->product_id = nProductId;
|
||||
|
|
@ -988,6 +1049,36 @@ extern "C"
|
|||
|
||||
int hid_init(void)
|
||||
{
|
||||
if ( !g_initialized )
|
||||
{
|
||||
// HIDAPI doesn't work well with Android < 4.3
|
||||
if (SDL_GetAndroidSDKVersion() >= 18) {
|
||||
// Make sure thread is attached to JVM/env
|
||||
JNIEnv *env;
|
||||
g_JVM->AttachCurrentThread( &env, NULL );
|
||||
pthread_setspecific( g_ThreadKey, (void*)env );
|
||||
|
||||
if ( !g_HIDDeviceManagerCallbackHandler )
|
||||
{
|
||||
LOGV( "hid_init() without callback handler" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bluetooth is currently only used for Steam Controllers, so check that hint
|
||||
// before initializing Bluetooth, which will prompt the user for permission.
|
||||
bool init_usb = true;
|
||||
bool init_bluetooth = false;
|
||||
if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE)) {
|
||||
if (SDL_GetAndroidSDKVersion() < 31 ||
|
||||
Android_JNI_RequestPermission("android.permission.BLUETOOTH_CONNECT")) {
|
||||
init_bluetooth = true;
|
||||
}
|
||||
}
|
||||
env->CallBooleanMethod( g_HIDDeviceManagerCallbackHandler, g_midHIDDeviceManagerInitialize, init_usb, init_bluetooth );
|
||||
ExceptionCheck( env, NULL, "hid_init" );
|
||||
}
|
||||
g_initialized = true; // Regardless of result, so it's only called once
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1035,7 +1126,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bEx
|
|||
hid_mutex_guard l( &g_DevicesMutex );
|
||||
for ( hid_device_ref<CHIDDevice> pCurr = g_Devices; pCurr; pCurr = pCurr->next )
|
||||
{
|
||||
if ( strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 )
|
||||
if ( SDL_strcmp( pCurr->GetDeviceInfo()->path, path ) == 0 )
|
||||
{
|
||||
hid_device *pValue = pCurr->GetDevice();
|
||||
if ( pValue )
|
||||
|
|
@ -1072,7 +1163,32 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned ch
|
|||
return -1; // Controller was disconnected
|
||||
}
|
||||
|
||||
// TODO: Implement timeout?
|
||||
static uint32_t getms()
|
||||
{
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
return (uint32_t)(now.tv_sec * 1000 + now.tv_usec / 1000);
|
||||
}
|
||||
|
||||
static void delayms(uint32_t ms)
|
||||
{
|
||||
int was_error;
|
||||
|
||||
struct timespec elapsed, tv;
|
||||
|
||||
/* Set the timeout interval */
|
||||
elapsed.tv_sec = ms / 1000;
|
||||
elapsed.tv_nsec = (ms % 1000) * 1000000;
|
||||
do {
|
||||
errno = 0;
|
||||
|
||||
tv.tv_sec = elapsed.tv_sec;
|
||||
tv.tv_nsec = elapsed.tv_nsec;
|
||||
was_error = nanosleep(&tv, &elapsed);
|
||||
} while (was_error && (errno == EINTR));
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
if ( device )
|
||||
|
|
@ -1081,7 +1197,17 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned ch
|
|||
hid_device_ref<CHIDDevice> pDevice = FindDevice( device->m_nId );
|
||||
if ( pDevice )
|
||||
{
|
||||
return pDevice->GetInput( data, length );
|
||||
int nResult = pDevice->GetInput( data, length );
|
||||
if ( nResult == 0 && milliseconds > 0 )
|
||||
{
|
||||
uint32_t start = getms();
|
||||
do
|
||||
{
|
||||
delayms( 1 );
|
||||
nResult = pDevice->GetInput( data, length );
|
||||
} while ( nResult == 0 && ( getms() - start ) < milliseconds );
|
||||
}
|
||||
return nResult;
|
||||
}
|
||||
LOGV( "controller was disconnected" );
|
||||
}
|
||||
|
|
@ -1213,3 +1339,80 @@ int hid_exit(void)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz);
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz);
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol );
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID);
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened);
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID);
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value);
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceRegisterCallback)(JNIEnv *env, jobject thiz )
|
||||
{
|
||||
LOGV("Stub HIDDeviceRegisterCallback()");
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
LOGV("Stub HIDDeviceReleaseCallback()");
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol )
|
||||
{
|
||||
LOGV("Stub HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID)
|
||||
{
|
||||
LOGV("Stub HIDDeviceOpenPending() id=%d\n", nDeviceID);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenResult)(JNIEnv *env, jobject thiz, int nDeviceID, bool bOpened)
|
||||
{
|
||||
LOGV("Stub HIDDeviceOpenResult() id=%d, result=%s\n", nDeviceID, bOpened ? "true" : "false");
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceDisconnected)(JNIEnv *env, jobject thiz, int nDeviceID)
|
||||
{
|
||||
LOGV("Stub HIDDeviceDisconnected() id=%d\n", nDeviceID);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceInputReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
|
||||
{
|
||||
LOGV("Stub HIDDeviceInput() id=%d len=%u\n", nDeviceID, nBufSize);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceFeatureReport)(JNIEnv *env, jobject thiz, int nDeviceID, jbyteArray value)
|
||||
{
|
||||
LOGV("Stub HIDDeviceFeatureReport() id=%d len=%u\n", nDeviceID, nBufSize);
|
||||
}
|
||||
|
||||
#endif /* SDL_HIDAPI_DISABLED */
|
||||
|
|
|
|||
|
|
@ -29,7 +29,12 @@
|
|||
|
||||
#include <wchar.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(NAMESPACE) && (0) /* SDL: don't export hidapi syms */
|
||||
#ifdef SDL_hidapi_h_
|
||||
#define SDL_HIDAPI_IMPLEMENTATION
|
||||
#define hid_device_info SDL_hid_device_info
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(NAMESPACE) && !defined(SDL_HIDAPI_IMPLEMENTATION) /* SDL: don't export hidapi syms */
|
||||
#define HID_API_EXPORT __declspec(dllexport)
|
||||
#define HID_API_CALL
|
||||
#else
|
||||
|
|
@ -53,6 +58,7 @@ namespace NAMESPACE {
|
|||
struct hid_device_;
|
||||
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
|
||||
|
||||
#ifndef SDL_HIDAPI_IMPLEMENTATION
|
||||
/** hidapi info structure */
|
||||
struct hid_device_info {
|
||||
/** Platform-specific device path */
|
||||
|
|
@ -77,9 +83,11 @@ namespace NAMESPACE {
|
|||
(Windows/Mac only).*/
|
||||
unsigned short usage;
|
||||
/** The USB interface which this logical device
|
||||
represents. Valid on both Linux implementations
|
||||
in all cases, and valid on the Windows implementation
|
||||
only if the device contains more than one interface. */
|
||||
represents.
|
||||
|
||||
* Valid on both Linux implementations in all cases.
|
||||
* Valid on the Windows implementation only if the device
|
||||
contains more than one interface. */
|
||||
int interface_number;
|
||||
|
||||
/** Additional information about the USB interface.
|
||||
|
|
@ -91,6 +99,7 @@ namespace NAMESPACE {
|
|||
/** Pointer to the next device */
|
||||
struct hid_device_info *next;
|
||||
};
|
||||
#endif /* !SDL_HIDAPI_IMPLEMENTATION */
|
||||
|
||||
|
||||
/** @brief Initialize the HIDAPI library.
|
||||
|
|
@ -101,7 +110,7 @@ namespace NAMESPACE {
|
|||
needed. This function should be called at the beginning of
|
||||
execution however, if there is a chance of HIDAPI handles
|
||||
being opened by different threads simultaneously.
|
||||
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
|
|
@ -139,7 +148,7 @@ namespace NAMESPACE {
|
|||
|
||||
@returns
|
||||
This function returns a pointer to a linked list of type
|
||||
struct #hid_device, containing information about the HID devices
|
||||
struct #hid_device_info, containing information about the HID devices
|
||||
attached to the system, or NULL in the case of failure. Free
|
||||
this linked list by calling hid_free_enumeration().
|
||||
*/
|
||||
|
|
@ -205,7 +214,7 @@ namespace NAMESPACE {
|
|||
the Control Endpoint (Endpoint 0).
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send.
|
||||
|
|
@ -214,7 +223,7 @@ namespace NAMESPACE {
|
|||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Read an Input report from a HID device with timeout.
|
||||
|
||||
|
|
@ -223,7 +232,7 @@ namespace NAMESPACE {
|
|||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
|
|
@ -235,7 +244,7 @@ namespace NAMESPACE {
|
|||
-1 on error. If no packet was available to be read within
|
||||
the timeout period, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds);
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||
|
||||
/** @brief Read an Input report from a HID device.
|
||||
|
||||
|
|
@ -244,7 +253,7 @@ namespace NAMESPACE {
|
|||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
|
|
@ -255,7 +264,7 @@ namespace NAMESPACE {
|
|||
-1 on error. If no packet was available to be read and
|
||||
the handle is in non-blocking mode, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Set the device handle to be non-blocking.
|
||||
|
||||
|
|
@ -267,7 +276,7 @@ namespace NAMESPACE {
|
|||
Nonblocking can be turned on and off at any time.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param nonblock enable or not the nonblocking reads
|
||||
- 1 to enable nonblocking
|
||||
- 0 to disable nonblocking.
|
||||
|
|
@ -275,7 +284,7 @@ namespace NAMESPACE {
|
|||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
|
||||
|
||||
/** @brief Send a Feature report to the device.
|
||||
|
||||
|
|
@ -293,7 +302,7 @@ namespace NAMESPACE {
|
|||
in would be 17.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send, including
|
||||
|
|
@ -303,7 +312,7 @@ namespace NAMESPACE {
|
|||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Get a feature report from a HID device.
|
||||
|
||||
|
|
@ -314,7 +323,7 @@ namespace NAMESPACE {
|
|||
start in data[1].
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into, including
|
||||
the Report ID. Set the first byte of @p data[] to the
|
||||
Report ID of the report to be read, or set it to zero
|
||||
|
|
@ -328,55 +337,55 @@ namespace NAMESPACE {
|
|||
one for the report ID (which is still in the first
|
||||
byte), or -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Close a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev);
|
||||
|
||||
/** @brief Get The Manufacturer String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Product String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Serial Number String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a string from a HID device, based on its string index.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string_index The index of the string to get.
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
|
@ -384,18 +393,22 @@ namespace NAMESPACE {
|
|||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a string describing the last error which occurred.
|
||||
|
||||
@ingroup API
|
||||
@param device A device handle returned from hid_open().
|
||||
@param dev A device handle returned from hid_open().
|
||||
|
||||
@returns
|
||||
This function returns a string containing the last error
|
||||
which occurred or NULL if none has occurred.
|
||||
*/
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
|
||||
|
||||
#if __IPHONEOS__ || __TVOS__
|
||||
HID_API_EXPORT void HID_API_CALL hid_ble_scan(int active);
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) && !defined(NAMESPACE)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,47 @@
|
|||
//======== Copyright (c) 2017 Valve Corporation, All rights reserved. =========
|
||||
//
|
||||
// Purpose: HID device abstraction temporary stub
|
||||
//
|
||||
//=============================================================================
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2021 Valve Corporation
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
#if !SDL_HIDAPI_DISABLED
|
||||
|
||||
#include "SDL_hints.h"
|
||||
|
||||
#define hid_init PLATFORM_hid_init
|
||||
#define hid_exit PLATFORM_hid_exit
|
||||
#define hid_enumerate PLATFORM_hid_enumerate
|
||||
#define hid_free_enumeration PLATFORM_hid_free_enumeration
|
||||
#define hid_open PLATFORM_hid_open
|
||||
#define hid_open_path PLATFORM_hid_open_path
|
||||
#define hid_write PLATFORM_hid_write
|
||||
#define hid_read_timeout PLATFORM_hid_read_timeout
|
||||
#define hid_read PLATFORM_hid_read
|
||||
#define hid_set_nonblocking PLATFORM_hid_set_nonblocking
|
||||
#define hid_send_feature_report PLATFORM_hid_send_feature_report
|
||||
#define hid_get_feature_report PLATFORM_hid_get_feature_report
|
||||
#define hid_close PLATFORM_hid_close
|
||||
#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string
|
||||
#define hid_get_product_string PLATFORM_hid_get_product_string
|
||||
#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string
|
||||
#define hid_get_indexed_string PLATFORM_hid_get_indexed_string
|
||||
#define hid_error PLATFORM_hid_error
|
||||
|
||||
#include <CoreBluetooth/CoreBluetooth.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
|
@ -193,24 +229,29 @@ typedef enum
|
|||
sharedInstance = [HIDBLEManager new];
|
||||
sharedInstance.nPendingScans = 0;
|
||||
sharedInstance.nPendingPairs = 0;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appWillResignActiveNotification:) name: UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appDidBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
|
||||
// receive reports on a high-priority serial-queue. optionally put writes on the serial queue to avoid logical
|
||||
// race conditions talking to the controller from multiple threads, although BLE fragmentation/assembly means
|
||||
// that we can still screw this up.
|
||||
// most importantly we need to consume reports at a high priority to avoid the OS thinking we aren't really
|
||||
// listening to the BLE device, as iOS on slower devices may stop delivery of packets to the app WITHOUT ACTUALLY
|
||||
// DISCONNECTING FROM THE DEVICE if we don't react quickly enough to their delivery.
|
||||
// see also the error-handling states in the peripheral delegate to re-open the device if it gets closed
|
||||
sharedInstance.bleSerialQueue = dispatch_queue_create( "com.valvesoftware.steamcontroller.ble", DISPATCH_QUEUE_SERIAL );
|
||||
dispatch_set_target_queue( sharedInstance.bleSerialQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
|
||||
// Bluetooth is currently only used for Steam Controllers, so check that hint
|
||||
// before initializing Bluetooth, which will prompt the user for permission.
|
||||
if ( SDL_GetHintBoolean( SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE ) )
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appWillResignActiveNotification:) name: UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(appDidBecomeActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
|
||||
// creating a CBCentralManager will always trigger a future centralManagerDidUpdateState:
|
||||
// where any scanning gets started or connecting to existing peripherals happens, it's never already in a
|
||||
// powered-on state for a newly launched application.
|
||||
sharedInstance.centralManager = [[CBCentralManager alloc] initWithDelegate:sharedInstance queue:sharedInstance.bleSerialQueue];
|
||||
// receive reports on a high-priority serial-queue. optionally put writes on the serial queue to avoid logical
|
||||
// race conditions talking to the controller from multiple threads, although BLE fragmentation/assembly means
|
||||
// that we can still screw this up.
|
||||
// most importantly we need to consume reports at a high priority to avoid the OS thinking we aren't really
|
||||
// listening to the BLE device, as iOS on slower devices may stop delivery of packets to the app WITHOUT ACTUALLY
|
||||
// DISCONNECTING FROM THE DEVICE if we don't react quickly enough to their delivery.
|
||||
// see also the error-handling states in the peripheral delegate to re-open the device if it gets closed
|
||||
sharedInstance.bleSerialQueue = dispatch_queue_create( "com.valvesoftware.steamcontroller.ble", DISPATCH_QUEUE_SERIAL );
|
||||
dispatch_set_target_queue( sharedInstance.bleSerialQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
|
||||
|
||||
// creating a CBCentralManager will always trigger a future centralManagerDidUpdateState:
|
||||
// where any scanning gets started or connecting to existing peripherals happens, it's never already in a
|
||||
// powered-on state for a newly launched application.
|
||||
sharedInstance.centralManager = [[CBCentralManager alloc] initWithDelegate:sharedInstance queue:sharedInstance.bleSerialQueue];
|
||||
}
|
||||
sharedInstance.deviceMap = [[NSMapTable alloc] initWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory capacity:4];
|
||||
});
|
||||
return sharedInstance;
|
||||
|
|
@ -250,6 +291,11 @@ typedef enum
|
|||
static uint64_t s_unLastUpdateTick = 0;
|
||||
static mach_timebase_info_data_t s_timebase_info;
|
||||
|
||||
if ( self.centralManager == nil )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s_timebase_info.denom == 0)
|
||||
{
|
||||
mach_timebase_info( &s_timebase_info );
|
||||
|
|
@ -295,6 +341,11 @@ typedef enum
|
|||
// manual API for folks to start & stop scanning
|
||||
- (void)startScan:(int)duration
|
||||
{
|
||||
if ( self.centralManager == nil )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog( @"BLE: requesting scan for %d seconds", duration );
|
||||
@synchronized (self)
|
||||
{
|
||||
|
|
@ -314,6 +365,11 @@ typedef enum
|
|||
|
||||
- (void)stopScan
|
||||
{
|
||||
if ( self.centralManager == nil )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog( @"BLE: stopping scan" );
|
||||
@synchronized (self)
|
||||
{
|
||||
|
|
@ -709,7 +765,7 @@ int HID_API_EXPORT HID_API_CALL hid_exit(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void HID_API_EXPORT HID_API_CALL hid_ble_scan( bool bStart )
|
||||
void HID_API_EXPORT HID_API_CALL hid_ble_scan( int bStart )
|
||||
{
|
||||
HIDBLEManager *bleManager = HIDBLEManager.sharedInstance;
|
||||
if ( bStart )
|
||||
|
|
@ -722,7 +778,12 @@ void HID_API_EXPORT HID_API_CALL hid_ble_scan( bool bStart )
|
|||
}
|
||||
}
|
||||
|
||||
hid_device * HID_API_EXPORT hid_open_path( const char *path, int bExclusive /* = false */ )
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path( const char *path, int bExclusive /* = false */ )
|
||||
{
|
||||
hid_device *result = NULL;
|
||||
NSString *nssPath = [NSString stringWithUTF8String:path];
|
||||
|
|
@ -836,6 +897,11 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s
|
|||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
||||
{
|
||||
HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle;
|
||||
|
|
@ -912,4 +978,9 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
|
|||
return result;
|
||||
}
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* !SDL_HIDAPI_DISABLED */
|
||||
|
|
|
|||
|
|
@ -31,12 +31,29 @@
|
|||
#include "SDL_thread.h"
|
||||
#include "SDL_mutex.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
#ifndef HAVE_WCSDUP
|
||||
#ifdef HAVE__WCSDUP
|
||||
#define wcsdup _wcsdup
|
||||
#else
|
||||
#define wcsdup _dupwcs
|
||||
static wchar_t *_dupwcs(const wchar_t *src)
|
||||
{
|
||||
wchar_t *dst = NULL;
|
||||
if (src) {
|
||||
size_t len = SDL_wcslen(src) + 1;
|
||||
len *= sizeof(wchar_t);
|
||||
dst = (wchar_t *) malloc(len);
|
||||
if (dst) memcpy(dst, src, len);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
#endif /* HAVE_WCSDUP */
|
||||
|
||||
#include <libusb.h>
|
||||
#include <locale.h> /* setlocale */
|
||||
|
||||
#include "hidapi.h"
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
||||
#ifdef NAMESPACE
|
||||
namespace NAMESPACE
|
||||
|
|
@ -57,12 +74,8 @@ typedef struct _SDL_ThreadBarrier
|
|||
|
||||
static int SDL_CreateThreadBarrier(SDL_ThreadBarrier *barrier, Uint32 count)
|
||||
{
|
||||
if (barrier == NULL) {
|
||||
return SDL_SetError("barrier must be non-NULL");
|
||||
}
|
||||
if (count == 0) {
|
||||
return SDL_SetError("count must be > 0");
|
||||
}
|
||||
SDL_assert(barrier != NULL);
|
||||
SDL_assert(count != 0);
|
||||
|
||||
barrier->mutex = SDL_CreateMutex();
|
||||
if (barrier->mutex == NULL) {
|
||||
|
|
@ -157,7 +170,7 @@ struct hid_device_ {
|
|||
SDL_cond *condition;
|
||||
SDL_ThreadBarrier barrier; /* Ensures correct startup sequence */
|
||||
int shutdown_thread;
|
||||
int cancelled;
|
||||
int transfer_loop_finished;
|
||||
struct libusb_transfer *transfer;
|
||||
|
||||
/* List of received input reports. */
|
||||
|
|
@ -196,7 +209,6 @@ static void free_hid_device(hid_device *dev)
|
|||
/*TODO: Implement this function on hidapi/libusb.. */
|
||||
static void register_error(hid_device *dev, const char *op)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -320,7 +332,6 @@ static inline int libusb_get_string_descriptor(libusb_device_handle *dev,
|
|||
(LIBUSB_DT_STRING << 8) | descriptor_index,
|
||||
lang_id, data, (uint16_t) length, 1000);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -373,12 +384,16 @@ static int is_language_supported(libusb_device_handle *dev, uint16_t lang)
|
|||
/* This function returns a newly allocated wide string containing the USB
|
||||
device string numbered by the index. The returned string must be freed
|
||||
by using free(). */
|
||||
#if defined(__OS2__) /* don't use iconv on OS/2: no support for wchar_t. */
|
||||
#define NO_ICONV
|
||||
#endif
|
||||
static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx)
|
||||
{
|
||||
char buf[512];
|
||||
int len;
|
||||
wchar_t *str = NULL;
|
||||
|
||||
#if !defined(NO_ICONV)
|
||||
wchar_t wbuf[256];
|
||||
SDL_iconv_t ic;
|
||||
size_t inbytes;
|
||||
|
|
@ -386,6 +401,9 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx)
|
|||
size_t res;
|
||||
const char *inptr;
|
||||
char *outptr;
|
||||
#else
|
||||
int i;
|
||||
#endif
|
||||
|
||||
/* Determine which language to use. */
|
||||
uint16_t lang;
|
||||
|
|
@ -402,6 +420,23 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx)
|
|||
if (len < 0)
|
||||
return NULL;
|
||||
|
||||
#if defined(NO_ICONV) /* original hidapi code for NO_ICONV : */
|
||||
|
||||
/* Bionic does not have wchar_t iconv support, so it
|
||||
has to be done manually. The following code will only work for
|
||||
code points that can be represented as a single UTF-16 character,
|
||||
and will incorrectly convert any code points which require more
|
||||
than one UTF-16 character.
|
||||
|
||||
Skip over the first character (2-bytes). */
|
||||
len -= 2;
|
||||
str = (wchar_t*) malloc((len / 2 + 1) * sizeof(wchar_t));
|
||||
for (i = 0; i < len / 2; i++) {
|
||||
str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8);
|
||||
}
|
||||
str[len / 2] = 0x00000000;
|
||||
|
||||
#else
|
||||
/* buf does not need to be explicitly NULL-terminated because
|
||||
it is only passed into iconv() which does not need it. */
|
||||
|
||||
|
|
@ -434,10 +469,100 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx)
|
|||
|
||||
err:
|
||||
SDL_iconv_close(ic);
|
||||
#endif
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
struct usb_string_cache_entry {
|
||||
uint16_t vid;
|
||||
uint16_t pid;
|
||||
wchar_t *vendor;
|
||||
wchar_t *product;
|
||||
};
|
||||
|
||||
static struct usb_string_cache_entry *usb_string_cache = NULL;
|
||||
static size_t usb_string_cache_size = 0;
|
||||
static size_t usb_string_cache_insert_pos = 0;
|
||||
|
||||
static int usb_string_cache_grow()
|
||||
{
|
||||
struct usb_string_cache_entry *new_cache;
|
||||
size_t allocSize;
|
||||
size_t new_cache_size;
|
||||
|
||||
new_cache_size = usb_string_cache_size + 8;
|
||||
allocSize = sizeof(struct usb_string_cache_entry) * new_cache_size;
|
||||
new_cache = (struct usb_string_cache_entry *)realloc(usb_string_cache, allocSize);
|
||||
if (!new_cache)
|
||||
return -1;
|
||||
|
||||
usb_string_cache = new_cache;
|
||||
usb_string_cache_size = new_cache_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_string_cache_destroy()
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < usb_string_cache_insert_pos; i++) {
|
||||
free(usb_string_cache[i].vendor);
|
||||
free(usb_string_cache[i].product);
|
||||
}
|
||||
free(usb_string_cache);
|
||||
|
||||
usb_string_cache = NULL;
|
||||
usb_string_cache_size = 0;
|
||||
usb_string_cache_insert_pos = 0;
|
||||
}
|
||||
|
||||
static struct usb_string_cache_entry *usb_string_cache_insert()
|
||||
{
|
||||
struct usb_string_cache_entry *new_entry = NULL;
|
||||
if (usb_string_cache_insert_pos >= usb_string_cache_size) {
|
||||
if (usb_string_cache_grow() < 0)
|
||||
return NULL;
|
||||
}
|
||||
new_entry = &usb_string_cache[usb_string_cache_insert_pos];
|
||||
usb_string_cache_insert_pos++;
|
||||
return new_entry;
|
||||
}
|
||||
|
||||
static const struct usb_string_cache_entry *usb_string_cache_find(struct libusb_device_descriptor *desc, struct libusb_device_handle *handle)
|
||||
{
|
||||
struct usb_string_cache_entry *entry = NULL;
|
||||
size_t i;
|
||||
|
||||
/* Search for existing string cache entry */
|
||||
for (i = 0; i < usb_string_cache_insert_pos; i++) {
|
||||
entry = &usb_string_cache[i];
|
||||
if (entry->vid != desc->idVendor)
|
||||
continue;
|
||||
if (entry->pid != desc->idProduct)
|
||||
continue;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Not found, create one. */
|
||||
entry = usb_string_cache_insert();
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
entry->vid = desc->idVendor;
|
||||
entry->pid = desc->idProduct;
|
||||
if (desc->iManufacturer > 0)
|
||||
entry->vendor = get_usb_string(handle, desc->iManufacturer);
|
||||
else
|
||||
entry->vendor = NULL;
|
||||
if (desc->iProduct > 0)
|
||||
entry->product = get_usb_string(handle, desc->iProduct);
|
||||
else
|
||||
entry->product = NULL;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static char *make_path(libusb_device *dev, int interface_number)
|
||||
{
|
||||
char str[64];
|
||||
|
|
@ -471,6 +596,8 @@ int HID_API_EXPORT hid_init(void)
|
|||
|
||||
int HID_API_EXPORT hid_exit(void)
|
||||
{
|
||||
usb_string_cache_destroy();
|
||||
|
||||
if (usb_context) {
|
||||
libusb_exit(usb_context);
|
||||
usb_context = NULL;
|
||||
|
|
@ -504,7 +631,9 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de
|
|||
0x15e4, /* Numark */
|
||||
0x162e, /* Joytech */
|
||||
0x1689, /* Razer Onza */
|
||||
0x1949, /* Lab126, Inc. */
|
||||
0x1bad, /* Harmonix */
|
||||
0x20d6, /* PowerA */
|
||||
0x24c6, /* PowerA */
|
||||
};
|
||||
|
||||
|
|
@ -524,17 +653,18 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de
|
|||
|
||||
static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
|
||||
{
|
||||
static const int XB1_IFACE_SUBCLASS = 71;
|
||||
static const int XB1_IFACE_PROTOCOL = 208;
|
||||
static const int SUPPORTED_VENDORS[] = {
|
||||
0x045e, /* Microsoft */
|
||||
0x0738, /* Mad Catz */
|
||||
0x0e6f, /* PDP */
|
||||
0x0f0d, /* Hori */
|
||||
0x1532, /* Razer Wildcat */
|
||||
0x24c6, /* PowerA */
|
||||
0x2e24, /* Hyperkin */
|
||||
};
|
||||
static const int XB1_IFACE_SUBCLASS = 71;
|
||||
static const int XB1_IFACE_PROTOCOL = 208;
|
||||
static const int SUPPORTED_VENDORS[] = {
|
||||
0x045e, /* Microsoft */
|
||||
0x0738, /* Mad Catz */
|
||||
0x0e6f, /* PDP */
|
||||
0x0f0d, /* Hori */
|
||||
0x1532, /* Razer Wildcat */
|
||||
0x20d6, /* PowerA */
|
||||
0x24c6, /* PowerA */
|
||||
0x2e24, /* Hyperkin */
|
||||
};
|
||||
|
||||
if (intf_desc->bInterfaceNumber == 0 &&
|
||||
intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
|
||||
|
|
@ -612,6 +742,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
|
||||
if (res >= 0) {
|
||||
struct hid_device_info *tmp;
|
||||
const struct usb_string_cache_entry *string_cache;
|
||||
|
||||
/* VID/PID match. Create the record. */
|
||||
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||
|
|
@ -633,12 +764,24 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
get_usb_string(handle, desc.iSerialNumber);
|
||||
|
||||
/* Manufacturer and Product strings */
|
||||
if (desc.iManufacturer > 0)
|
||||
cur_dev->manufacturer_string =
|
||||
get_usb_string(handle, desc.iManufacturer);
|
||||
if (desc.iProduct > 0)
|
||||
cur_dev->product_string =
|
||||
get_usb_string(handle, desc.iProduct);
|
||||
if (dev_vid && dev_pid) {
|
||||
string_cache = usb_string_cache_find(&desc, handle);
|
||||
if (string_cache) {
|
||||
if (string_cache->vendor) {
|
||||
cur_dev->manufacturer_string = wcsdup(string_cache->vendor);
|
||||
}
|
||||
if (string_cache->product) {
|
||||
cur_dev->product_string = wcsdup(string_cache->product);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (desc.iManufacturer > 0)
|
||||
cur_dev->manufacturer_string =
|
||||
get_usb_string(handle, desc.iManufacturer);
|
||||
if (desc.iProduct > 0)
|
||||
cur_dev->product_string =
|
||||
get_usb_string(handle, desc.iProduct);
|
||||
}
|
||||
|
||||
#ifdef INVASIVE_GET_USAGE
|
||||
{
|
||||
|
|
@ -825,13 +968,9 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
|
|||
}
|
||||
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||
dev->shutdown_thread = 1;
|
||||
dev->cancelled = 1;
|
||||
return;
|
||||
}
|
||||
else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
|
||||
dev->shutdown_thread = 1;
|
||||
dev->cancelled = 1;
|
||||
return;
|
||||
}
|
||||
else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
|
||||
//LOG("Timeout (normal)\n");
|
||||
|
|
@ -840,12 +979,17 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
|
|||
LOG("Unknown transfer code: %d\n", transfer->status);
|
||||
}
|
||||
|
||||
if (dev->shutdown_thread) {
|
||||
dev->transfer_loop_finished = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Re-submit the transfer object. */
|
||||
res = libusb_submit_transfer(transfer);
|
||||
if (res != 0) {
|
||||
LOG("Unable to submit URB. libusb error code: %d\n", res);
|
||||
dev->shutdown_thread = 1;
|
||||
dev->cancelled = 1;
|
||||
dev->transfer_loop_finished = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -888,6 +1032,7 @@ static int read_thread(void *param)
|
|||
res != LIBUSB_ERROR_TIMEOUT &&
|
||||
res != LIBUSB_ERROR_OVERFLOW &&
|
||||
res != LIBUSB_ERROR_INTERRUPTED) {
|
||||
dev->shutdown_thread = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -897,8 +1042,8 @@ static int read_thread(void *param)
|
|||
if no transfers are pending, but that's OK. */
|
||||
libusb_cancel_transfer(dev->transfer);
|
||||
|
||||
while (!dev->cancelled)
|
||||
libusb_handle_events_completed(usb_context, &dev->cancelled);
|
||||
while (!dev->transfer_loop_finished)
|
||||
libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished);
|
||||
|
||||
/* Now that the read thread is stopping, Wake any threads which are
|
||||
waiting on data (in hid_read_timeout()). Do this under a mutex to
|
||||
|
|
@ -920,35 +1065,46 @@ static int read_thread(void *param)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void init_xboxone(libusb_device_handle *device_handle, struct libusb_config_descriptor *conf_desc)
|
||||
static void init_xboxone(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, struct libusb_config_descriptor *conf_desc)
|
||||
{
|
||||
static const int XB1_IFACE_SUBCLASS = 71;
|
||||
static const int XB1_IFACE_PROTOCOL = 208;
|
||||
static const int VENDOR_MICROSOFT = 0x045e;
|
||||
static const int XB1_IFACE_SUBCLASS = 71;
|
||||
static const int XB1_IFACE_PROTOCOL = 208;
|
||||
int j, k, res;
|
||||
|
||||
for (j = 0; j < conf_desc->bNumInterfaces; j++) {
|
||||
const struct libusb_interface *intf = &conf_desc->interface[j];
|
||||
for (k = 0; k < intf->num_altsetting; k++) {
|
||||
const struct libusb_interface_descriptor *intf_desc;
|
||||
intf_desc = &intf->altsetting[k];
|
||||
|
||||
if (intf_desc->bInterfaceNumber != 0 &&
|
||||
intf_desc->bAlternateSetting == 0 &&
|
||||
intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
|
||||
const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k];
|
||||
if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC &&
|
||||
intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS &&
|
||||
intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) {
|
||||
res = libusb_claim_interface(device_handle, intf_desc->bInterfaceNumber);
|
||||
if (res < 0) {
|
||||
LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
|
||||
continue;
|
||||
int bSetAlternateSetting = 0;
|
||||
|
||||
/* Newer Microsoft Xbox One controllers have a high speed alternate setting */
|
||||
if (idVendor == VENDOR_MICROSOFT &&
|
||||
intf_desc->bInterfaceNumber == 0 && intf_desc->bAlternateSetting == 1) {
|
||||
bSetAlternateSetting = 1;
|
||||
} else if (intf_desc->bInterfaceNumber != 0 && intf_desc->bAlternateSetting == 0) {
|
||||
bSetAlternateSetting = 1;
|
||||
}
|
||||
|
||||
res = libusb_set_interface_alt_setting(device_handle, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting);
|
||||
if (res < 0) {
|
||||
LOG("xbox init: can't set alt setting %d: %d\n", intf_desc->bInterfaceNumber, res);
|
||||
}
|
||||
if (bSetAlternateSetting) {
|
||||
res = libusb_claim_interface(device_handle, intf_desc->bInterfaceNumber);
|
||||
if (res < 0) {
|
||||
LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
|
||||
continue;
|
||||
}
|
||||
|
||||
libusb_release_interface(device_handle, intf_desc->bInterfaceNumber);
|
||||
LOG("Setting alternate setting for VID/PID 0x%x/0x%x interface %d to %d\n", idVendor, idProduct, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting);
|
||||
|
||||
res = libusb_set_interface_alt_setting(device_handle, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting);
|
||||
if (res < 0) {
|
||||
LOG("xbox init: can't set alt setting %d: %d\n", intf_desc->bInterfaceNumber, res);
|
||||
}
|
||||
|
||||
libusb_release_interface(device_handle, intf_desc->bInterfaceNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1030,7 +1186,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
|
|||
|
||||
/* Initialize XBox One controllers */
|
||||
if (is_xboxone(desc.idVendor, intf_desc)) {
|
||||
init_xboxone(dev->device_handle, conf_desc);
|
||||
init_xboxone(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc);
|
||||
}
|
||||
|
||||
/* Store off the string descriptor indexes */
|
||||
|
|
@ -1173,21 +1329,21 @@ static void cleanup_mutex(void *param)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
int bytes_read = -1;
|
||||
|
||||
#if 0
|
||||
int transferred;
|
||||
int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000);
|
||||
LOG("transferred: %d\n", transferred);
|
||||
return transferred;
|
||||
#endif
|
||||
int bytes_read;
|
||||
|
||||
SDL_LockMutex(dev->mutex);
|
||||
/* TODO: pthread_cleanup SDL? */
|
||||
|
||||
bytes_read = -1;
|
||||
|
||||
/* There's an input report queued up. Return it. */
|
||||
if (dev->input_reports) {
|
||||
/* Return the first one */
|
||||
|
|
@ -1620,5 +1776,3 @@ uint16_t get_usb_code_for_current_locale(void)
|
|||
#ifdef NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
|
|
|||
|
|
@ -22,8 +22,6 @@
|
|||
********************************************************/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
|
||||
#endif
|
||||
|
|
@ -50,7 +48,7 @@
|
|||
#include <linux/input.h>
|
||||
#include <libudev.h>
|
||||
|
||||
#include "hidapi.h"
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
||||
#ifdef NAMESPACE
|
||||
namespace NAMESPACE
|
||||
|
|
@ -217,7 +215,7 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
|
|||
* strings pointed to by serial_number_utf8 and product_name_utf8 after use.
|
||||
*/
|
||||
static int
|
||||
parse_uevent_info(const char *uevent, int *bus_type,
|
||||
parse_uevent_info(const char *uevent, unsigned *bus_type,
|
||||
unsigned short *vendor_id, unsigned short *product_id,
|
||||
char **serial_number_utf8, char **product_name_utf8)
|
||||
{
|
||||
|
|
@ -300,7 +298,7 @@ static int is_BLE(hid_device *dev)
|
|||
if (hid_dev) {
|
||||
unsigned short dev_vid = 0;
|
||||
unsigned short dev_pid = 0;
|
||||
int bus_type = 0;
|
||||
unsigned bus_type = 0;
|
||||
char *serial_number_utf8 = NULL;
|
||||
char *product_name_utf8 = NULL;
|
||||
|
||||
|
|
@ -338,8 +336,8 @@ static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t
|
|||
struct udev_device *udev_dev, *parent, *hid_dev;
|
||||
struct stat s;
|
||||
int ret = -1;
|
||||
char *serial_number_utf8 = NULL;
|
||||
char *product_name_utf8 = NULL;
|
||||
char *serial_number_utf8 = NULL;
|
||||
char *product_name_utf8 = NULL;
|
||||
char *tmp;
|
||||
|
||||
/* Create the udev object */
|
||||
|
|
@ -365,7 +363,7 @@ static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t
|
|||
if (hid_dev) {
|
||||
unsigned short dev_vid;
|
||||
unsigned short dev_pid;
|
||||
int bus_type;
|
||||
unsigned bus_type;
|
||||
size_t retm;
|
||||
|
||||
ret = parse_uevent_info(
|
||||
|
|
@ -433,8 +431,8 @@ static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t
|
|||
}
|
||||
|
||||
end:
|
||||
free(serial_number_utf8);
|
||||
free(product_name_utf8);
|
||||
free(serial_number_utf8);
|
||||
free(product_name_utf8);
|
||||
|
||||
udev_device_unref(udev_dev);
|
||||
/* parent and hid_dev don't need to be (and can't be) unref'd.
|
||||
|
|
@ -503,7 +501,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
unsigned short dev_pid;
|
||||
char *serial_number_utf8 = NULL;
|
||||
char *product_name_utf8 = NULL;
|
||||
int bus_type;
|
||||
unsigned bus_type;
|
||||
int result;
|
||||
|
||||
/* Get the filename of the /sys entry for the device
|
||||
|
|
@ -716,7 +714,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
|
|||
dev = new_hid_device();
|
||||
|
||||
/* OPEN HERE */
|
||||
dev->device_handle = open(path, O_RDWR);
|
||||
dev->device_handle = open(path, O_RDWR | O_CLOEXEC);
|
||||
|
||||
/* If we have a good handle, return it. */
|
||||
if (dev->device_handle >= 0) {
|
||||
|
|
@ -828,34 +826,47 @@ int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
|
|||
return 0; /* Success */
|
||||
}
|
||||
|
||||
|
||||
int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
|
||||
{
|
||||
static const int MAX_RETRIES = 50;
|
||||
int retry;
|
||||
int res;
|
||||
|
||||
res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
|
||||
if (res < 0)
|
||||
perror("ioctl (SFEATURE)");
|
||||
for (retry = 0; retry < MAX_RETRIES; ++retry) {
|
||||
res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
|
||||
if (res < 0 && errno == EPIPE) {
|
||||
/* Try again... */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
perror("ioctl (SFEATURE)");
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
||||
{
|
||||
int res;
|
||||
unsigned char report = data[0];
|
||||
|
||||
/* It looks like HIDIOCGFEATURE() on Bluetooth LE devices doesn't return the report number */
|
||||
if (dev->needs_ble_hack) {
|
||||
data[1] = data[0];
|
||||
++data;
|
||||
--length;
|
||||
}
|
||||
res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
|
||||
if (res < 0)
|
||||
perror("ioctl (GFEATURE)");
|
||||
else if (dev->needs_ble_hack)
|
||||
++res;
|
||||
|
||||
else if (dev->needs_ble_hack) {
|
||||
/* Versions of BlueZ before 5.56 don't include the report in the data,
|
||||
* and versions of BlueZ >= 5.56 include 2 copies of the report.
|
||||
* We'll fix it so that there is a single copy of the report in both cases
|
||||
*/
|
||||
if (data[0] == report && data[1] == report) {
|
||||
memmove(&data[0], &data[1], res);
|
||||
} else if (data[0] != report) {
|
||||
memmove(&data[1], &data[0], res);
|
||||
data[0] = report;
|
||||
++res;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -886,6 +897,10 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s
|
|||
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
(void)dev;
|
||||
(void)string_index;
|
||||
(void)string;
|
||||
(void)maxlen;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -898,5 +913,3 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
|||
#ifdef NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
|
|
|||
|
|
@ -1,333 +0,0 @@
|
|||
//=================== Copyright Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: A wrapper around both the libusb and hidraw versions of HIDAPI
|
||||
//
|
||||
// The libusb version doesn't support Bluetooth, but not all Linux
|
||||
// distributions allow access to /dev/hidraw*
|
||||
//
|
||||
// This merges the two, at a small performance cost, until distributions
|
||||
// have granted access to /dev/hidraw*
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#define NAMESPACE HIDRAW
|
||||
#include "../hidapi/hidapi.h"
|
||||
#undef NAMESPACE
|
||||
#undef HIDAPI_H__
|
||||
|
||||
#define NAMESPACE HIDUSB
|
||||
#include "../hidapi/hidapi.h"
|
||||
#undef NAMESPACE
|
||||
#undef HIDAPI_H__
|
||||
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
||||
#include "../../../public/tier1/utlvector.h"
|
||||
#include "../../../public/tier1/utlhashmap.h"
|
||||
|
||||
|
||||
template <class T>
|
||||
void CopyHIDDeviceInfo( T *pSrc, struct hid_device_info *pDst )
|
||||
{
|
||||
pDst->path = pSrc->path ? strdup( pSrc->path ) : NULL;
|
||||
pDst->vendor_id = pSrc->vendor_id;
|
||||
pDst->product_id = pSrc->product_id;
|
||||
pDst->serial_number = pSrc->serial_number ? wcsdup( pSrc->serial_number ) : NULL;
|
||||
pDst->release_number = pSrc->release_number;
|
||||
pDst->manufacturer_string = pSrc->manufacturer_string ? wcsdup( pSrc->manufacturer_string ) : NULL;
|
||||
pDst->product_string = pSrc->product_string ? wcsdup( pSrc->product_string ) : NULL;
|
||||
pDst->usage_page = pSrc->usage_page;
|
||||
pDst->usage = pSrc->usage;
|
||||
pDst->interface_number = pSrc->interface_number;
|
||||
pDst->next = NULL;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
enum EHIDAPIType
|
||||
{
|
||||
k_EHIDAPIUnknown,
|
||||
k_EHIDAPIRAW,
|
||||
k_EHIDAPIUSB
|
||||
};
|
||||
|
||||
static CUtlHashMap<uintptr_t, EHIDAPIType> s_hashDeviceToAPI;
|
||||
|
||||
static EHIDAPIType GetAPIForDevice( hid_device *pDevice )
|
||||
{
|
||||
int iIndex = s_hashDeviceToAPI.Find( (uintptr_t)pDevice );
|
||||
if ( iIndex != -1 )
|
||||
{
|
||||
return s_hashDeviceToAPI[ iIndex ];
|
||||
}
|
||||
return k_EHIDAPIUnknown;
|
||||
}
|
||||
|
||||
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||
{
|
||||
struct HIDUSB::hid_device_info *usb_devs = HIDUSB::hid_enumerate( vendor_id, product_id );
|
||||
struct HIDUSB::hid_device_info *usb_dev;
|
||||
struct HIDRAW::hid_device_info *raw_devs = HIDRAW::hid_enumerate( vendor_id, product_id );
|
||||
struct HIDRAW::hid_device_info *raw_dev;
|
||||
struct hid_device_info *devs = NULL, *last = NULL, *new_dev;
|
||||
|
||||
for ( usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next )
|
||||
{
|
||||
bool bFound = false;
|
||||
for ( raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next )
|
||||
{
|
||||
if ( usb_dev->vendor_id == raw_dev->vendor_id && usb_dev->product_id == raw_dev->product_id )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("%s USB device VID/PID 0x%.4x/0x%.4x, %ls %ls\n", bFound ? "Found matching" : "Added new", usb_dev->vendor_id, usb_dev->product_id, usb_dev->manufacturer_string, usb_dev->product_string );
|
||||
|
||||
if ( !bFound )
|
||||
{
|
||||
new_dev = new struct hid_device_info;
|
||||
CopyHIDDeviceInfo( usb_dev, new_dev );
|
||||
|
||||
if ( last )
|
||||
{
|
||||
last->next = new_dev;
|
||||
}
|
||||
else
|
||||
{
|
||||
devs = new_dev;
|
||||
}
|
||||
last = new_dev;
|
||||
}
|
||||
}
|
||||
HIDUSB::hid_free_enumeration( usb_devs );
|
||||
|
||||
for ( raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next )
|
||||
{
|
||||
new_dev = new struct hid_device_info;
|
||||
CopyHIDDeviceInfo( raw_dev, new_dev );
|
||||
new_dev->next = NULL;
|
||||
|
||||
if ( last )
|
||||
{
|
||||
last->next = new_dev;
|
||||
}
|
||||
else
|
||||
{
|
||||
devs = new_dev;
|
||||
}
|
||||
last = new_dev;
|
||||
}
|
||||
HIDRAW::hid_free_enumeration( raw_devs );
|
||||
|
||||
return devs;
|
||||
}
|
||||
|
||||
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
|
||||
{
|
||||
while ( devs )
|
||||
{
|
||||
struct hid_device_info *next = devs->next;
|
||||
free( devs->path );
|
||||
free( devs->serial_number );
|
||||
free( devs->manufacturer_string );
|
||||
free( devs->product_string );
|
||||
delete devs;
|
||||
devs = next;
|
||||
}
|
||||
}
|
||||
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
||||
{
|
||||
hid_device *pDevice = NULL;
|
||||
if ( ( pDevice = (hid_device *)HIDRAW::hid_open( vendor_id, product_id, serial_number ) ) != NULL )
|
||||
{
|
||||
s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIRAW );
|
||||
return pDevice;
|
||||
}
|
||||
if ( ( pDevice = (hid_device *)HIDUSB::hid_open( vendor_id, product_id, serial_number ) ) != NULL )
|
||||
{
|
||||
s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIUSB );
|
||||
return pDevice;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bExclusive)
|
||||
{
|
||||
hid_device *pDevice = NULL;
|
||||
if ( ( pDevice = (hid_device *)HIDRAW::hid_open_path( path, bExclusive ) ) != NULL )
|
||||
{
|
||||
s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIRAW );
|
||||
return pDevice;
|
||||
}
|
||||
if ( ( pDevice = (hid_device *)HIDUSB::hid_open_path( path, bExclusive ) ) != NULL )
|
||||
{
|
||||
s_hashDeviceToAPI.Insert( (uintptr_t)pDevice, k_EHIDAPIUSB );
|
||||
return pDevice;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_write( (HIDRAW::hid_device*)device, data, length );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_write( (HIDUSB::hid_device*)device, data, length );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_read_timeout( (HIDRAW::hid_device*)device, data, length, milliseconds );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_read_timeout( (HIDUSB::hid_device*)device, data, length, milliseconds );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_read( (HIDRAW::hid_device*)device, data, length );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_read( (HIDUSB::hid_device*)device, data, length );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_set_nonblocking( (HIDRAW::hid_device*)device, nonblock );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_set_nonblocking( (HIDUSB::hid_device*)device, nonblock );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_send_feature_report( (HIDRAW::hid_device*)device, data, length );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_send_feature_report( (HIDUSB::hid_device*)device, data, length );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_get_feature_report( (HIDRAW::hid_device*)device, data, length );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_get_feature_report( (HIDUSB::hid_device*)device, data, length );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
HIDRAW::hid_close( (HIDRAW::hid_device*)device );
|
||||
break;
|
||||
case k_EHIDAPIUSB:
|
||||
HIDUSB::hid_close( (HIDUSB::hid_device*)device );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
s_hashDeviceToAPI.Remove( (uintptr_t)device );
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_get_manufacturer_string( (HIDRAW::hid_device*)device, string, maxlen );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_get_manufacturer_string( (HIDUSB::hid_device*)device, string, maxlen );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_get_product_string( (HIDRAW::hid_device*)device, string, maxlen );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_get_product_string( (HIDUSB::hid_device*)device, string, maxlen );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_get_serial_number_string( (HIDRAW::hid_device*)device, string, maxlen );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_get_serial_number_string( (HIDUSB::hid_device*)device, string, maxlen );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_get_indexed_string( (HIDRAW::hid_device*)device, string_index, string, maxlen );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_get_indexed_string( (HIDUSB::hid_device*)device, string_index, string, maxlen );
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device)
|
||||
{
|
||||
switch ( GetAPIForDevice( device ) )
|
||||
{
|
||||
case k_EHIDAPIRAW:
|
||||
return HIDRAW::hid_error( (HIDRAW::hid_device*)device );
|
||||
case k_EHIDAPIUSB:
|
||||
return HIDUSB::hid_error( (HIDUSB::hid_device*)device );
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,8 +21,6 @@
|
|||
********************************************************/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
/* See Apple Technical Note TN2187 for details on IOHidManager. */
|
||||
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
|
|
@ -33,7 +31,9 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "hidapi.h"
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
||||
#define VALVE_USB_VID 0x28DE
|
||||
|
||||
/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
|
||||
It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
|
||||
|
|
@ -251,12 +251,15 @@ static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t
|
|||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
|
||||
if (CFGetTypeID(prop) != CFStringGetTypeID())
|
||||
return 0;
|
||||
|
||||
str = (CFStringRef)IOHIDDeviceGetProperty(device, prop);
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
if (str) {
|
||||
if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
|
||||
len --;
|
||||
|
||||
CFIndex str_len = CFStringGetLength(str);
|
||||
|
|
@ -288,11 +291,14 @@ static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, cha
|
|||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (CFGetTypeID(prop) != CFStringGetTypeID())
|
||||
return 0;
|
||||
|
||||
str = (CFStringRef)IOHIDDeviceGetProperty(device, prop);
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
if (str) {
|
||||
if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
|
||||
len--;
|
||||
|
||||
CFIndex str_len = CFStringGetLength(str);
|
||||
|
|
@ -392,20 +398,86 @@ static void hid_device_removal_callback(void *context, IOReturn result,
|
|||
}
|
||||
}
|
||||
|
||||
static CFDictionaryRef
|
||||
create_usage_match(const UInt32 page, const UInt32 usage, int *okay)
|
||||
{
|
||||
CFDictionaryRef retval = NULL;
|
||||
CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
|
||||
CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
|
||||
const void *keys[2] = { (void *) CFSTR(kIOHIDDeviceUsagePageKey), (void *) CFSTR(kIOHIDDeviceUsageKey) };
|
||||
const void *vals[2] = { (void *) pageNumRef, (void *) usageNumRef };
|
||||
|
||||
if (pageNumRef && usageNumRef) {
|
||||
retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
}
|
||||
|
||||
if (pageNumRef) {
|
||||
CFRelease(pageNumRef);
|
||||
}
|
||||
if (usageNumRef) {
|
||||
CFRelease(usageNumRef);
|
||||
}
|
||||
|
||||
if (!retval) {
|
||||
*okay = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static CFDictionaryRef
|
||||
create_vendor_match(const UInt32 vendor, int *okay)
|
||||
{
|
||||
CFDictionaryRef retval = NULL;
|
||||
CFNumberRef vidNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor);
|
||||
const void *keys[1] = { (void *) CFSTR(kIOHIDVendorIDKey) };
|
||||
const void *vals[1] = { (void *) vidNumRef };
|
||||
|
||||
if (vidNumRef) {
|
||||
retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFRelease(vidNumRef);
|
||||
}
|
||||
|
||||
if (!retval) {
|
||||
*okay = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
|
||||
static int init_hid_manager(void)
|
||||
{
|
||||
int okay = 1;
|
||||
const void *vals[] = {
|
||||
(void *) create_usage_match(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
|
||||
(void *) create_usage_match(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
|
||||
(void *) create_usage_match(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
|
||||
(void *) create_vendor_match(VALVE_USB_VID, &okay),
|
||||
};
|
||||
const size_t numElements = SDL_arraysize(vals);
|
||||
CFArrayRef matchingArray = okay ? CFArrayCreate(kCFAllocatorDefault, vals, numElements, &kCFTypeArrayCallBacks) : NULL;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < numElements; i++) {
|
||||
if (vals[i]) {
|
||||
CFRelease((CFTypeRef) vals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize all the HID Manager Objects */
|
||||
hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
||||
if (hid_mgr) {
|
||||
IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
|
||||
IOHIDManagerSetDeviceMatchingMultiple(hid_mgr, matchingArray);
|
||||
IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
if (matchingArray != NULL) {
|
||||
CFRelease(matchingArray);
|
||||
}
|
||||
|
||||
return hid_mgr ? 0 : -1;
|
||||
}
|
||||
|
||||
/* Initialize the IOHIDManager if necessary. This is the public function, and
|
||||
|
|
@ -482,6 +554,17 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
continue;
|
||||
}
|
||||
|
||||
#if 0 // Prefer direct HID support as that has extended functionality
|
||||
#if defined(SDL_JOYSTICK_MFI)
|
||||
// We want to prefer Game Controller support where available,
|
||||
// as Apple will likely be requiring that for supported devices.
|
||||
extern SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device);
|
||||
if (IOS_SupportedHIDDevice(dev)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
dev_vid = get_vendor_id(dev);
|
||||
dev_pid = get_product_id(dev);
|
||||
|
||||
|
|
@ -1173,5 +1256,3 @@ int main(void)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
|
|
|||
|
|
@ -21,10 +21,12 @@
|
|||
********************************************************/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef _WIN32_WINNT_WIN8
|
||||
#define _WIN32_WINNT_WIN8 0x0602
|
||||
#endif
|
||||
|
||||
#if 0 /* can cause redefinition errors on some toolchains */
|
||||
#ifdef __MINGW32__
|
||||
#include <ntdef.h>
|
||||
|
|
@ -51,7 +53,6 @@ typedef LONG NTSTATUS;
|
|||
#define memset SDL_memset
|
||||
#define strcmp SDL_strcmp
|
||||
#define strlen SDL_strlen
|
||||
#define strncpy SDL_strlcpy
|
||||
#define strstr SDL_strstr
|
||||
#define strtol SDL_strtol
|
||||
#define wcscmp SDL_wcscmp
|
||||
|
|
@ -93,8 +94,8 @@ extern "C" {
|
|||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/*#include <stdio.h>*/
|
||||
/*#include <stdlib.h>*/
|
||||
|
||||
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
|
@ -103,8 +104,8 @@ extern "C" {
|
|||
#define MIN(x,y) ((x) < (y)? (x): (y))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Thanks Microsoft, but I know how to use strncpy(). */
|
||||
#pragma warning(disable:4996)
|
||||
/* Yes, we have some unreferenced formal parameters */
|
||||
#pragma warning(disable:4100)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -176,8 +177,29 @@ struct hid_device_ {
|
|||
char *read_buf;
|
||||
OVERLAPPED ol;
|
||||
OVERLAPPED write_ol;
|
||||
BOOL use_hid_write_output_report;
|
||||
};
|
||||
|
||||
static BOOL
|
||||
IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
|
||||
{
|
||||
OSVERSIONINFOEXW osvi;
|
||||
DWORDLONG const dwlConditionMask = VerSetConditionMask(
|
||||
VerSetConditionMask(
|
||||
VerSetConditionMask(
|
||||
0, VER_MAJORVERSION, VER_GREATER_EQUAL ),
|
||||
VER_MINORVERSION, VER_GREATER_EQUAL ),
|
||||
VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );
|
||||
|
||||
memset(&osvi, 0, sizeof(osvi));
|
||||
osvi.dwOSVersionInfoSize = sizeof( osvi );
|
||||
osvi.dwMajorVersion = wMajorVersion;
|
||||
osvi.dwMinorVersion = wMinorVersion;
|
||||
osvi.wServicePackMajor = wServicePackMajor;
|
||||
|
||||
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
|
||||
}
|
||||
|
||||
static hid_device *new_hid_device()
|
||||
{
|
||||
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
|
||||
|
|
@ -242,7 +264,7 @@ static void register_error(hid_device *device, const char *op)
|
|||
#ifndef HIDAPI_USE_DDK
|
||||
static int lookup_functions()
|
||||
{
|
||||
lib_handle = LoadLibraryA("hid.dll");
|
||||
lib_handle = LoadLibrary(TEXT("hid.dll"));
|
||||
if (lib_handle) {
|
||||
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
|
||||
RESOLVE(HidD_GetAttributes);
|
||||
|
|
@ -321,6 +343,7 @@ int hid_blacklist(unsigned short vendor_id, unsigned short product_id)
|
|||
{ 0x1532, 0x0109 }, /* Razer Lycosa Gaming keyboard */
|
||||
{ 0x1532, 0x010B }, /* Razer Arctosa Gaming keyboard */
|
||||
{ 0x045E, 0x0822 }, /* Microsoft Precision Mouse */
|
||||
{ 0x0D8C, 0x0014 }, /* Sharkoon Skiller SGH2 headset */
|
||||
|
||||
/* Turns into an Android controller when enumerated... */
|
||||
{ 0x0738, 0x2217 } /* SPEEDLINK COMPETITION PRO */
|
||||
|
|
@ -523,8 +546,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
|
|||
if (str) {
|
||||
len = strlen(str);
|
||||
cur_dev->path = (char*) calloc(len+1, sizeof(char));
|
||||
strncpy(cur_dev->path, str, len+1);
|
||||
cur_dev->path[len] = '\0';
|
||||
memcpy(cur_dev->path, str, len+1);
|
||||
}
|
||||
else
|
||||
cur_dev->path = NULL;
|
||||
|
|
@ -692,6 +714,11 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bEx
|
|||
dev->input_report_length = caps.InputReportByteLength;
|
||||
HidD_FreePreparsedData(pp_data);
|
||||
|
||||
/* On Windows 7, we need to use hid_write_output_report() over Bluetooth */
|
||||
if (dev->output_report_length > 512) {
|
||||
dev->use_hid_write_output_report = !IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN8 ), LOBYTE( _WIN32_WINNT_WIN8 ), 0 );
|
||||
}
|
||||
|
||||
dev->read_buf = (char*) malloc(dev->input_report_length);
|
||||
|
||||
return dev;
|
||||
|
|
@ -717,17 +744,12 @@ static int hid_write_timeout(hid_device *dev, const unsigned char *data, size_t
|
|||
{
|
||||
DWORD bytes_written;
|
||||
BOOL res;
|
||||
size_t stashed_length = length;
|
||||
unsigned char *buf;
|
||||
|
||||
#if 1
|
||||
/* If the application is writing to the device, it knows how much data to write.
|
||||
* This matches the behavior on other platforms. It's also important when writing
|
||||
* to Sony game controllers over Bluetooth, where there's a CRC at the end which
|
||||
* must not be tampered with.
|
||||
*/
|
||||
buf = (unsigned char *) data;
|
||||
#else
|
||||
if (dev->use_hid_write_output_report) {
|
||||
return hid_write_output_report(dev, data, length);
|
||||
}
|
||||
|
||||
/* Make sure the right number of bytes are passed to WriteFile. Windows
|
||||
expects the number of bytes which are in the _longest_ report (plus
|
||||
one for the report number) bytes even if the data is a report
|
||||
|
|
@ -745,42 +767,35 @@ static int hid_write_timeout(hid_device *dev, const unsigned char *data, size_t
|
|||
memset(buf + length, 0, dev->output_report_length - length);
|
||||
length = dev->output_report_length;
|
||||
}
|
||||
#endif
|
||||
if (length > 512)
|
||||
{
|
||||
return hid_write_output_report( dev, data, stashed_length );
|
||||
}
|
||||
else
|
||||
{
|
||||
res = WriteFile( dev->device_handle, buf, ( DWORD ) length, NULL, &dev->write_ol );
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
/* WriteFile() failed. Return error. */
|
||||
register_error(dev, "WriteFile");
|
||||
bytes_written = (DWORD) -1;
|
||||
goto end_of_function;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait here until the write is done. This makes
|
||||
hid_write() synchronous. */
|
||||
res = WaitForSingleObject(dev->write_ol.hEvent, milliseconds);
|
||||
if (res != WAIT_OBJECT_0)
|
||||
{
|
||||
// There was a Timeout.
|
||||
bytes_written = (DWORD) -1;
|
||||
register_error(dev, "WriteFile/WaitForSingleObject Timeout");
|
||||
goto end_of_function;
|
||||
}
|
||||
|
||||
res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*F=don't_wait*/);
|
||||
if (!res) {
|
||||
/* The Write operation failed. */
|
||||
res = WriteFile( dev->device_handle, buf, ( DWORD ) length, NULL, &dev->write_ol );
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
/* WriteFile() failed. Return error. */
|
||||
register_error(dev, "WriteFile");
|
||||
bytes_written = (DWORD) -1;
|
||||
goto end_of_function;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait here until the write is done. This makes hid_write() synchronous. */
|
||||
res = WaitForSingleObject(dev->write_ol.hEvent, milliseconds);
|
||||
if (res != WAIT_OBJECT_0)
|
||||
{
|
||||
// There was a Timeout.
|
||||
bytes_written = (DWORD) -1;
|
||||
register_error(dev, "WriteFile/WaitForSingleObject Timeout");
|
||||
goto end_of_function;
|
||||
}
|
||||
|
||||
res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*F=don't_wait*/);
|
||||
if (!res) {
|
||||
/* The Write operation failed. */
|
||||
register_error(dev, "WriteFile");
|
||||
bytes_written = (DWORD) -1;
|
||||
goto end_of_function;
|
||||
}
|
||||
|
||||
end_of_function:
|
||||
if (buf != data)
|
||||
free(buf);
|
||||
|
|
@ -820,20 +835,19 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char
|
|||
}
|
||||
}
|
||||
|
||||
if (milliseconds >= 0) {
|
||||
/* See if there is any data yet. */
|
||||
res = WaitForSingleObject(ev, milliseconds);
|
||||
if (res != WAIT_OBJECT_0) {
|
||||
/* There was no data this time. Return zero bytes available,
|
||||
but leave the Overlapped I/O running. */
|
||||
return 0;
|
||||
}
|
||||
/* See if there is any data yet. */
|
||||
res = WaitForSingleObject(ev, milliseconds >= 0 ? milliseconds : INFINITE);
|
||||
if (res != WAIT_OBJECT_0) {
|
||||
/* There was no data this time. Return zero bytes available,
|
||||
but leave the Overlapped I/O running. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Either WaitForSingleObject() told us that ReadFile has completed, or
|
||||
we are in non-blocking mode. Get the number of bytes read. The actual
|
||||
data has been copied to the data[] array which was passed to ReadFile(). */
|
||||
res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
|
||||
/* Get the number of bytes read. The actual data has been copied to the data[]
|
||||
array which was passed to ReadFile(). We must not wait here because we've
|
||||
already waited on our event above, and since it's auto-reset, it will have
|
||||
been reset back to unsignalled by now. */
|
||||
res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, FALSE/*don't wait*/);
|
||||
|
||||
/* Set pending back to false, even if GetOverlappedResult() returned error. */
|
||||
dev->read_pending = FALSE;
|
||||
|
|
@ -932,9 +946,23 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned
|
|||
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
|
||||
{
|
||||
typedef BOOL (WINAPI *CancelIoEx_t)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
|
||||
CancelIoEx_t CancelIoExFunc = (CancelIoEx_t)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CancelIoEx");
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
CancelIo(dev->device_handle);
|
||||
|
||||
if (CancelIoExFunc) {
|
||||
CancelIoExFunc(dev->device_handle, NULL);
|
||||
} else {
|
||||
/* Windows XP, this will only cancel I/O on the current thread */
|
||||
CancelIo(dev->device_handle);
|
||||
}
|
||||
if (dev->read_pending) {
|
||||
DWORD bytes_read = 0;
|
||||
|
||||
GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
|
||||
}
|
||||
free_hid_device(dev);
|
||||
}
|
||||
|
||||
|
|
@ -1064,5 +1092,3 @@ int __cdecl main(int argc, char* argv[])
|
|||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue