From e9f6805fc67d69b087e243aee642a4e0c906b385 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 21 Aug 2018 19:42:19 -0700 Subject: [PATCH] Removed dependency on C++ runtime on iOS --- Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj | 10 +- src/hidapi/ios/{hid.mm => hid.m} | 206 +++++++++++--------- 2 files changed, 122 insertions(+), 94 deletions(-) rename src/hidapi/ios/{hid.mm => hid.m} (84%) diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj index aa20507b8..ee00dd906 100755 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj @@ -203,7 +203,8 @@ F30D9CA5212CD0BF0047DF2E /* SDL_coremotionsensor.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */; }; F30D9CA6212CD0BF0047DF2E /* SDL_coremotionsensor.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */; }; F30D9CA7212CD0BF0047DF2E /* SDL_coremotionsensor.h in Headers */ = {isa = PBXBuildFile; fileRef = F30D9CA4212CD0BF0047DF2E /* SDL_coremotionsensor.h */; }; - F3BDD77620F51C3C004ECBF3 /* hid.mm in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD77520F51C3C004ECBF3 /* hid.mm */; }; + F30D9CC6212CE92C0047DF2E /* hid.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CC5212CE92C0047DF2E /* hid.m */; }; + F30D9CC7212CE92C0047DF2E /* hid.m in Sources */ = {isa = PBXBuildFile; fileRef = F30D9CC5212CE92C0047DF2E /* hid.m */; }; F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; }; F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; }; F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; }; @@ -535,7 +536,7 @@ F30D9C9D212CD0990047DF2E /* SDL_sensor.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sensor.c; sourceTree = ""; }; F30D9CA3212CD0BF0047DF2E /* SDL_coremotionsensor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_coremotionsensor.m; sourceTree = ""; }; F30D9CA4212CD0BF0047DF2E /* SDL_coremotionsensor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_coremotionsensor.h; sourceTree = ""; }; - F3BDD77520F51C3C004ECBF3 /* hid.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = hid.mm; sourceTree = ""; }; + F30D9CC5212CE92C0047DF2E /* hid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = hid.m; sourceTree = ""; }; F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = ""; }; F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = ""; }; F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = ""; }; @@ -859,7 +860,7 @@ F3BDD77420F51C18004ECBF3 /* ios */ = { isa = PBXGroup; children = ( - F3BDD77520F51C3C004ECBF3 /* hid.mm */, + F30D9CC5212CE92C0047DF2E /* hid.m */, ); path = ios; sourceTree = ""; @@ -1520,6 +1521,7 @@ FAB598421BB5C31500BE72C5 /* SDL_quit.c in Sources */, FAB598441BB5C31500BE72C5 /* SDL_touch.c in Sources */, FAB598461BB5C31500BE72C5 /* SDL_windowevents.c in Sources */, + F30D9CC7212CE92C0047DF2E /* hid.m in Sources */, FAB598491BB5C31600BE72C5 /* SDL_rwopsbundlesupport.m in Sources */, FAB5984A1BB5C31600BE72C5 /* SDL_rwops.c in Sources */, FAB5984B1BB5C31600BE72C5 /* SDL_sysfilesystem.m in Sources */, @@ -1631,6 +1633,7 @@ FD6526750DE8FCDD002AD96B /* SDL_windowevents.c in Sources */, 4D7516FB1EE1C28A00820EEA /* SDL_uikitmetalview.m in Sources */, FD6526760DE8FCDD002AD96B /* SDL_rwops.c in Sources */, + F30D9CC6212CE92C0047DF2E /* hid.m in Sources */, 4D7517201EE1D98200820EEA /* SDL_vulkan_utils.c in Sources */, FD6526780DE8FCDD002AD96B /* SDL_error.c in Sources */, FD65267A0DE8FCDD002AD96B /* SDL.c in Sources */, @@ -1716,7 +1719,6 @@ AA628ADB159369E3005138DD /* SDL_rotate.c in Sources */, AA126AD51617C5E7005ABC8F /* SDL_uikitmodes.m in Sources */, AA704DD7162AA90A0076D1C1 /* SDL_dropevents.c in Sources */, - F3BDD77620F51C3C004ECBF3 /* hid.mm in Sources */, AABCC3951640643D00AB8930 /* SDL_uikitmessagebox.m in Sources */, AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */, AA0F8495178D5F1A00823F9D /* SDL_systls.c in Sources */, diff --git a/src/hidapi/ios/hid.mm b/src/hidapi/ios/hid.m similarity index 84% rename from src/hidapi/ios/hid.mm rename to src/hidapi/ios/hid.m index bf96440e6..1e206bfae 100644 --- a/src/hidapi/ios/hid.mm +++ b/src/hidapi/ios/hid.m @@ -36,7 +36,9 @@ typedef uint64_t uint64; // TODO: create CBUUID's in __attribute__((constructor)) rather than doing [CBUUID UUIDWithString:...] everywhere #pragma pack(push,1) -struct bluetoothSegment { + +typedef struct +{ uint8_t segmentHeader; uint8_t featureReportMessageID; uint8_t length; @@ -47,11 +49,9 @@ struct bluetoothSegment { uint64_t ulPayload; uint8_t ucPayload[15]; }; - - size_t size() { return length + 3; } -}; +} bluetoothSegment; -struct hidFeatureReport { +typedef struct { uint8_t id; union { bluetoothSegment segment; @@ -68,71 +68,83 @@ struct hidFeatureReport { }; }; }; -}; +} hidFeatureReport; + #pragma pack(pop) -template -struct RingBuffer { +size_t GetBluetoothSegmentSize(bluetoothSegment *segment) +{ + return segment->length + 3; +} + +#define RingBuffer_cbElem 19 +#define RingBuffer_nElem 4096 + +typedef struct { int _first, _last; - uint8_t _data[ ( nElem * cbElem ) ]; + uint8_t _data[ ( RingBuffer_nElem * RingBuffer_cbElem ) ]; pthread_mutex_t accessLock; +} RingBuffer; + +static void RingBuffer_init( RingBuffer *this ) +{ + this->_first = -1; + this->_last = 0; + pthread_mutex_init( &this->accessLock, 0 ); +} - RingBuffer() { _first = -1; _last = 0; pthread_mutex_init( &accessLock, 0 ); } - - bool write( const T *src ) - { - pthread_mutex_lock( &accessLock ); - memcpy( &_data[ _last ], src, cbElem ); - if ( _first == -1 ) - { - _first = _last; - } - _last = ( _last + cbElem ) % (nElem * cbElem); - if ( _last == _first ) - { - _first = ( _first + cbElem ) % (nElem * cbElem); - pthread_mutex_unlock( &accessLock ); - return false; - } - pthread_mutex_unlock( &accessLock ); - return true; - } - - bool read( T *dst ) - { - pthread_mutex_lock( &accessLock ); - if ( _first == -1 ) - { - pthread_mutex_unlock( &accessLock ); - return false; - } - memcpy( dst, &_data[ _first ], cbElem ); - _first = ( _first + cbElem ) % (nElem * cbElem); - if ( _first == _last ) - { - _first = -1; - } - pthread_mutex_unlock( &accessLock ); - return true; - } - -}; +static bool RingBuffer_write( RingBuffer *this, const uint8_t *src ) +{ + pthread_mutex_lock( &this->accessLock ); + memcpy( &this->_data[ this->_last ], src, RingBuffer_cbElem ); + if ( this->_first == -1 ) + { + this->_first = this->_last; + } + this->_last = ( this->_last + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem); + if ( this->_last == this->_first ) + { + this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem); + pthread_mutex_unlock( &this->accessLock ); + return false; + } + pthread_mutex_unlock( &this->accessLock ); + return true; +} + +static bool RingBuffer_read( RingBuffer *this, uint8_t *dst ) +{ + pthread_mutex_lock( &this->accessLock ); + if ( this->_first == -1 ) + { + pthread_mutex_unlock( &this->accessLock ); + return false; + } + memcpy( dst, &this->_data[ this->_first ], RingBuffer_cbElem ); + this->_first = ( this->_first + RingBuffer_cbElem ) % (RingBuffer_nElem * RingBuffer_cbElem); + if ( this->_first == this->_last ) + { + this->_first = -1; + } + pthread_mutex_unlock( &this->accessLock ); + return true; +} #pragma mark HIDBLEDevice Definition -enum BLEDeviceWaitState +typedef enum { - None, - Waiting, - Complete, - Error -}; + BLEDeviceWaitState_None, + BLEDeviceWaitState_Waiting, + BLEDeviceWaitState_Complete, + BLEDeviceWaitState_Error +} BLEDeviceWaitState; @interface HIDBLEDevice : NSObject { - RingBuffer _inputReports; - uint8_t _featureReport[20]; + RingBuffer _inputReports; + uint8_t _featureReport[20]; BLEDeviceWaitState _waitStateForReadFeatureReport; BLEDeviceWaitState _waitStateForWriteFeatureReport; } @@ -419,6 +431,7 @@ static void process_pending_events() { if ( self = [super init] ) { + RingBuffer_init( &_inputReports ); self.bleSteamController = nil; self.bleCharacteristicInput = nil; self.bleCharacteristicReport = nil; @@ -432,6 +445,7 @@ static void process_pending_events() { if ( self = [super init] ) { + RingBuffer_init( &_inputReports ); _connected = NO; _ready = NO; self.bleSteamController = peripheral; @@ -460,7 +474,7 @@ static void process_pending_events() - (size_t)read_input_report:(uint8_t *)dst { - if ( _inputReports.read( dst+1 ) ) + if ( RingBuffer_read( &_inputReports, dst+1 ) ) { *dst = 0x03; return 20; @@ -479,14 +493,14 @@ static void process_pending_events() #if FEATURE_REPORT_LOGGING uint8_t *reportBytes = (uint8_t *)report; - NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", report->segment.size(), + NSLog( @"HIDBLE:send_feature_report (%02zu/19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", GetBluetoothSegmentSize( report->segment ), reportBytes[1], reportBytes[2], reportBytes[3], reportBytes[4], reportBytes[5], reportBytes[6], reportBytes[7], reportBytes[8], reportBytes[9], reportBytes[10], reportBytes[11], reportBytes[12], reportBytes[13], reportBytes[14], reportBytes[15], reportBytes[16], reportBytes[17], reportBytes[18], reportBytes[19] ); #endif - int sendSize = (int)report->segment.size(); + int sendSize = (int)GetBluetoothSegmentSize( &report->segment ); if ( sendSize > 20 ) sendSize = 20; @@ -501,43 +515,43 @@ static void process_pending_events() #else // this is technically the correct send_feature_report logic if you want to make sure it gets through and is // acknowledged or errors out - _waitStateForWriteFeatureReport = BLEDeviceWaitState::Waiting; + _waitStateForWriteFeatureReport = BLEDeviceWaitState_Waiting; [_bleSteamController writeValue:[NSData dataWithBytes:&report->segment length:sendSize ] forCharacteristic:_bleCharacteristicReport type:CBCharacteristicWriteWithResponse]; - while ( _waitStateForWriteFeatureReport == BLEDeviceWaitState::Waiting ) + while ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Waiting ) { process_pending_events(); } - if ( _waitStateForWriteFeatureReport == BLEDeviceWaitState::Error ) + if ( _waitStateForWriteFeatureReport == BLEDeviceWaitState_Error ) { - _waitStateForWriteFeatureReport = BLEDeviceWaitState::None; + _waitStateForWriteFeatureReport = BLEDeviceWaitState_None; return -1; } - _waitStateForWriteFeatureReport = BLEDeviceWaitState::None; + _waitStateForWriteFeatureReport = BLEDeviceWaitState_None; return 19; #endif } - (int)get_feature_report:(uint8_t)feature into:(uint8_t *)buffer { - _waitStateForReadFeatureReport = BLEDeviceWaitState::Waiting; + _waitStateForReadFeatureReport = BLEDeviceWaitState_Waiting; [_bleSteamController readValueForCharacteristic:_bleCharacteristicReport]; - while ( _waitStateForReadFeatureReport == BLEDeviceWaitState::Waiting ) + while ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Waiting ) process_pending_events(); - if ( _waitStateForReadFeatureReport == BLEDeviceWaitState::Error ) + if ( _waitStateForReadFeatureReport == BLEDeviceWaitState_Error ) { - _waitStateForReadFeatureReport = BLEDeviceWaitState::None; + _waitStateForReadFeatureReport = BLEDeviceWaitState_None; return -1; } memcpy( buffer, _featureReport, sizeof(_featureReport) ); - _waitStateForReadFeatureReport = BLEDeviceWaitState::None; + _waitStateForReadFeatureReport = BLEDeviceWaitState_None; #if FEATURE_REPORT_LOGGING NSLog( @"HIDBLE:get_feature_report (19) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", @@ -617,7 +631,7 @@ static void process_pending_events() { NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 19", (unsigned long)data.length ); } - if ( !_inputReports.write( (const uint8_t *)data.bytes ) ) + if ( !RingBuffer_write( &_inputReports, (const uint8_t *)data.bytes ) ) { uint64_t ticksNow = mach_approximate_time(); if ( ticksNow - s_ticksLastOverflowReport > (5ull * NSEC_PER_SEC / 10) ) @@ -634,7 +648,7 @@ static void process_pending_events() if ( error != nil ) { NSLog( @"HIDBLE: get_feature_report error: %@", error ); - _waitStateForReadFeatureReport = BLEDeviceWaitState::Error; + _waitStateForReadFeatureReport = BLEDeviceWaitState_Error; } else { @@ -644,7 +658,7 @@ static void process_pending_events() NSLog( @"HIDBLE: incoming data is %lu bytes should be exactly 20", (unsigned long)data.length ); } memcpy( _featureReport, data.bytes, MIN( data.length, sizeof(_featureReport) ) ); - _waitStateForReadFeatureReport = BLEDeviceWaitState::Complete; + _waitStateForReadFeatureReport = BLEDeviceWaitState_Complete; } } } @@ -656,11 +670,11 @@ static void process_pending_events() if ( error != nil ) { NSLog( @"HIDBLE: write_feature_report error: %@", error ); - _waitStateForWriteFeatureReport = BLEDeviceWaitState::Error; + _waitStateForWriteFeatureReport = BLEDeviceWaitState_Error; } else { - _waitStateForWriteFeatureReport = BLEDeviceWaitState::Complete; + _waitStateForWriteFeatureReport = BLEDeviceWaitState_Complete; } } } @@ -676,7 +690,7 @@ static void process_pending_events() #pragma mark hid_api implementation struct hid_device_ { - HIDBLEDevice *device_handle; + void *device_handle; int blocking; hid_device *next; }; @@ -721,7 +735,7 @@ hid_device * HID_API_EXPORT hid_open_path( const char *path, int bExclusive /* = { result = (hid_device *)malloc( sizeof( hid_device ) ); memset( result, 0, sizeof( hid_device ) ); - result->device_handle = device; + result->device_handle = (void*)CFBridgingRetain( device ); result->blocking = NO; // enable reporting input events on the characteristic [device.bleSteamController setNotifyValue:YES forCharacteristic:device.bleCharacteristicInput]; @@ -783,8 +797,8 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, } continue; } - hid_device_info *device_info = (hid_device_info *)malloc( sizeof(hid_device_info) ); - memset( device_info, 0, sizeof(hid_device_info) ); + struct hid_device_info *device_info = (struct hid_device_info *)malloc( sizeof(struct hid_device_info) ); + memset( device_info, 0, sizeof(struct hid_device_info) ); device_info->next = root; root = device_info; device_info->path = strdup( device.bleSteamController.identifier.UUIDString.UTF8String ); @@ -820,17 +834,21 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { - if ( !dev->device_handle.connected ) + HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle; + + if ( !device_handle.connected ) return -1; - return [dev->device_handle send_report:data length:length]; + return [device_handle send_report:data length:length]; } void HID_API_EXPORT hid_close(hid_device *dev) { + HIDBLEDevice *device_handle = CFBridgingRelease( dev->device_handle ); + // disable reporting input events on the characteristic - if ( dev->device_handle.connected ) { - [dev->device_handle.bleSteamController setNotifyValue:NO forCharacteristic:dev->device_handle.bleCharacteristicInput]; + if ( device_handle.connected ) { + [device_handle.bleSteamController setNotifyValue:NO forCharacteristic:device_handle.bleCharacteristicInput]; } free( dev ); @@ -838,25 +856,31 @@ void HID_API_EXPORT hid_close(hid_device *dev) int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) { - if ( !dev->device_handle.connected ) + HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle; + + if ( !device_handle.connected ) return -1; - return [dev->device_handle send_feature_report:(hidFeatureReport *)(void *)data]; + return [device_handle send_feature_report:(hidFeatureReport *)(void *)data]; } int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) { - if ( !dev->device_handle.connected ) + HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle; + + if ( !device_handle.connected ) return -1; - size_t written = [dev->device_handle get_feature_report:data[0] into:data]; + size_t written = [device_handle get_feature_report:data[0] into:data]; return written == length-1 ? (int)length : (int)written; } int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) { - if ( !dev->device_handle.connected ) + HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle; + + if ( !device_handle.connected ) return -1; return hid_read_timeout(dev, data, length, 0); @@ -864,14 +888,16 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { - if ( !dev->device_handle.connected ) + HIDBLEDevice *device_handle = (__bridge HIDBLEDevice *)dev->device_handle; + + if ( !device_handle.connected ) return -1; if ( milliseconds != 0 ) { NSLog( @"hid_read_timeout with non-zero wait" ); } - int result = (int)[dev->device_handle read_input_report:data]; + int result = (int)[device_handle read_input_report:data]; #if FEATURE_REPORT_LOGGING NSLog( @"HIDBLE:hid_read_timeout (%d) [%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x]", result, data[1], data[2], data[3], data[4], data[5], data[6],