mirror of
https://github.com/OpenEmu/OpenEmu-SDK.git
synced 2025-11-01 10:15:12 +00:00
663 lines
24 KiB
Objective-C
663 lines
24 KiB
Objective-C
/*
|
|
Copyright (c) 2009, OpenEmu Team
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the OpenEmu Team nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#import "OEDeviceManager.h"
|
|
|
|
#import "OEDeviceHandler.h"
|
|
#import "OEControllerDescription.h"
|
|
#import "OEHIDDeviceHandler.h"
|
|
#import "OEMultiHIDDeviceHandler.h"
|
|
#import "OEWiimoteHIDDeviceHandler.h"
|
|
#import "OEPS3HIDDeviceHandler.h"
|
|
#import "OEXBox360HIDDeviceHander.h"
|
|
#import "OEHIDEvent.h"
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
#import <IOBluetooth/IOBluetooth.h>
|
|
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
|
|
#import <IOBluetooth/objc/IOBluetoothDevice.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
NSString *const OEWiimoteSupportEnabled = @"OEWiimoteSupportEnabled";
|
|
|
|
NSString *const OEDeviceManagerDidAddDeviceHandlerNotification = @"OEDeviceManagerDidAddDeviceHandlerNotification";
|
|
NSString *const OEDeviceManagerDidRemoveDeviceHandlerNotification = @"OEDeviceManagerDidRemoveDeviceHandlerNotification";
|
|
NSString *const OEDeviceManagerDeviceHandlerUserInfoKey = @"OEDeviceManagerDeviceHandlerUserInfoKey";
|
|
|
|
@interface _OEDeviceManagerEventMonitor : NSObject
|
|
+ (instancetype)monitorWithGlobalMonitorHandler:(BOOL(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler;
|
|
+ (instancetype)monitorWithEventMonitorHandler:(void(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler;
|
|
@property(copy) BOOL(^globalMonitor)(OEDeviceHandler *handler, OEHIDEvent *event);
|
|
@property(copy) void(^eventMonitor)(OEDeviceHandler *handler, OEHIDEvent *event);
|
|
@end
|
|
|
|
static void OEHandle_DeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
|
|
|
|
static BOOL OE_isWiimoteControllerName(NSString *name)
|
|
{
|
|
return [name hasPrefix:@"Nintendo RVL-CNT-01"];
|
|
}
|
|
|
|
static BOOL OE_isPS3ControllerName(NSString *name)
|
|
{
|
|
return [name hasPrefix:@"PLAYSTATION(R)3 Controller"];
|
|
}
|
|
|
|
static BOOL OE_isXboxControllerName(NSString *name)
|
|
{
|
|
return [name isEqualToString:@"Controller"];
|
|
}
|
|
|
|
static const void * kOEBluetoothDevicePairSyncStyleKey = &kOEBluetoothDevicePairSyncStyleKey;
|
|
|
|
@interface IOBluetoothDevicePair (SyncStyle)
|
|
@property(nonatomic, assign) BOOL attemptedHostToDevice;
|
|
@end
|
|
|
|
@implementation IOBluetoothDevicePair (SyncStyle)
|
|
|
|
- (BOOL)attemptedHostToDevice
|
|
{
|
|
return [objc_getAssociatedObject(self, kOEBluetoothDevicePairSyncStyleKey) boolValue];
|
|
}
|
|
|
|
- (void)setAttemptedHostToDevice:(BOOL)attemptedHostToDevice
|
|
{
|
|
objc_setAssociatedObject(self, kOEBluetoothDevicePairSyncStyleKey, @(attemptedHostToDevice), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
}
|
|
|
|
@end
|
|
|
|
@interface OEDeviceManager () <IOBluetoothDeviceInquiryDelegate>
|
|
@end
|
|
|
|
@interface OEDeviceHandler ()
|
|
@property(readwrite) NSUInteger deviceNumber;
|
|
@property(readwrite) NSUInteger deviceIdentifier;
|
|
@end
|
|
|
|
@implementation OEDeviceManager {
|
|
dispatch_queue_t _uniqueIdentifiersToDeviceHandlersQueue;
|
|
NSMutableDictionary<NSString *, __kindof OEDeviceHandler *> *_uniqueIdentifiersToDeviceHandlers;
|
|
|
|
NSMutableSet<OEDeviceHandler *> *_keyboardHandlers;
|
|
NSMutableSet<OEDeviceHandler *> *_deviceHandlers;
|
|
NSMutableSet<OEMultiHIDDeviceHandler *> *_multiDeviceHandlers;
|
|
IOHIDManagerRef _hidManager;
|
|
|
|
id _keyEventMonitor;
|
|
id _modifierMaskMonitor;
|
|
|
|
IOBluetoothDeviceInquiry *_inquiry;
|
|
|
|
NSUInteger _lastAttributedDeviceIdentifier;
|
|
NSUInteger _lastAttributedMultiDeviceIdentifier;
|
|
NSUInteger _lastAttributedKeyboardIdentifier;
|
|
|
|
NSHashTable<_OEDeviceManagerEventMonitor *> *_globalEventListeners;
|
|
NSHashTable<_OEDeviceManagerEventMonitor *> *_unhandledEventListeners;
|
|
NSMutableDictionary<OEDeviceHandler *, NSHashTable<_OEDeviceManagerEventMonitor *> *> *_deviceHandlersToEventListeners;
|
|
}
|
|
|
|
+ (OEDeviceManager *)sharedDeviceManager;
|
|
{
|
|
static OEDeviceManager *sharedHIDManager = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
sharedHIDManager = [[self alloc] init];
|
|
});
|
|
|
|
return sharedHIDManager;
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
if((self = [super init]))
|
|
{
|
|
_uniqueIdentifiersToDeviceHandlersQueue = dispatch_queue_create("org.openemu.uniqueIdentifiersToDeviceHandlersQueue", DISPATCH_QUEUE_CONCURRENT);
|
|
_uniqueIdentifiersToDeviceHandlers = [[NSMutableDictionary alloc] init];
|
|
|
|
_keyboardHandlers = [[NSMutableSet alloc] init];
|
|
_deviceHandlers = [[NSMutableSet alloc] init];
|
|
_multiDeviceHandlers = [[NSMutableSet alloc] init];
|
|
_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
|
|
|
_globalEventListeners = [NSHashTable weakObjectsHashTable];
|
|
_unhandledEventListeners = [NSHashTable weakObjectsHashTable];
|
|
_deviceHandlersToEventListeners = [NSMutableDictionary dictionary];
|
|
|
|
IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, OEHandle_DeviceMatchingCallback, (__bridge void *)self);
|
|
|
|
IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
|
|
|
|
[self OE_addKeyboardEventMonitor];
|
|
|
|
NSArray *matchingTypes = @[ @{
|
|
@ kIOHIDDeviceUsagePageKey : @(kHIDPage_GenericDesktop),
|
|
@ kIOHIDDeviceUsageKey : @(kHIDUsage_GD_Joystick)
|
|
},
|
|
@{
|
|
@ kIOHIDDeviceUsagePageKey : @(kHIDPage_GenericDesktop),
|
|
@ kIOHIDDeviceUsageKey : @(kHIDUsage_GD_GamePad)
|
|
},
|
|
@{
|
|
@ kIOHIDDeviceUsagePageKey : @(kHIDPage_GenericDesktop),
|
|
@ kIOHIDDeviceUsageKey : @(kHIDUsage_GD_Keyboard)
|
|
} ];
|
|
|
|
IOHIDManagerSetDeviceMatchingMultiple(_hidManager, (__bridge CFArrayRef)matchingTypes);
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OE_wiimoteDeviceDidDisconnect:) name:OEWiimoteDeviceHandlerDidDisconnectNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OE_applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)OE_applicationWillTerminate:(NSNotification *)notification;
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
// We do this before release to ensure that we've properly cleaned up our HIDManager references and removed our devices from the runloop.
|
|
for(OEDeviceHandler *handler in [_deviceHandlers copy])
|
|
[self OE_removeDeviceHandler:handler];
|
|
|
|
[NSEvent removeMonitor:_keyEventMonitor];
|
|
[NSEvent removeMonitor:_modifierMaskMonitor];
|
|
_keyEventMonitor = nil;
|
|
_modifierMaskMonitor = nil;
|
|
|
|
if(_hidManager == NULL)
|
|
return;
|
|
|
|
IOHIDManagerUnscheduleFromRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
|
|
CFRelease(_hidManager);
|
|
}
|
|
|
|
- (NSArray<OEDeviceHandler *> *)deviceHandlers
|
|
{
|
|
return [_deviceHandlers copy];
|
|
}
|
|
|
|
- (NSArray<OEDeviceHandler *> *)controllerDeviceHandlers
|
|
{
|
|
return [[_deviceHandlers allObjects] sortedArrayUsingComparator:^ NSComparisonResult (OEDeviceHandler *obj1, OEDeviceHandler *obj2) {
|
|
return [@([obj1 deviceIdentifier]) compare:@([obj2 deviceIdentifier])];
|
|
}];
|
|
}
|
|
|
|
- (NSArray<OEDeviceHandler *> *)keyboardDeviceHandlers
|
|
{
|
|
return [[_keyboardHandlers allObjects] sortedArrayUsingComparator:^ NSComparisonResult (OEDeviceHandler *obj1, OEDeviceHandler *obj2) {
|
|
return [@([obj1 deviceIdentifier]) compare:@([obj2 deviceIdentifier])];
|
|
}];
|
|
}
|
|
|
|
- (OEDeviceHandler *)deviceHandlerForUniqueIdentifier:(NSString *)uniqueIdentifier
|
|
{
|
|
__block OEDeviceHandler *handler;
|
|
|
|
dispatch_sync(_uniqueIdentifiersToDeviceHandlersQueue, ^{
|
|
handler = _uniqueIdentifiersToDeviceHandlers[uniqueIdentifier];
|
|
|
|
if (handler)
|
|
return;
|
|
|
|
handler = [[OEDeviceHandlerPlaceholder alloc] initWithUniqueIdentifier:uniqueIdentifier];
|
|
_uniqueIdentifiersToDeviceHandlers[uniqueIdentifier] = handler;
|
|
});
|
|
|
|
return handler;
|
|
}
|
|
|
|
- (void)deviceHandler:(nullable OEDeviceHandler *)device didReceiveEvent:(OEHIDEvent *)event
|
|
{
|
|
BOOL continuePosting = YES;
|
|
for(_OEDeviceManagerEventMonitor *monitor in _globalEventListeners) {
|
|
if()
|
|
continuePosting = NO;
|
|
}
|
|
|
|
if(!continuePosting)
|
|
return;
|
|
|
|
if(device != nil) {
|
|
NSHashTable<_OEDeviceManagerEventMonitor *> *monitors = _deviceHandlersToEventListeners[device];
|
|
for(_OEDeviceManagerEventMonitor *monitor in monitors) {
|
|
[monitor eventMonitor](device, event);
|
|
continuePosting = NO;
|
|
}
|
|
|
|
if(!continuePosting)
|
|
return;
|
|
}
|
|
|
|
for(_OEDeviceManagerEventMonitor *monitor in _unhandledEventListeners)
|
|
[monitor eventMonitor](device, event);
|
|
}
|
|
|
|
- (BOOL)hasEventMonitor
|
|
{
|
|
return _globalEventListeners.count > 0;
|
|
}
|
|
|
|
- (id)addGlobalEventMonitorHandler:(BOOL(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler;
|
|
{
|
|
_OEDeviceManagerEventMonitor *monitor = [_OEDeviceManagerEventMonitor monitorWithGlobalMonitorHandler:handler];
|
|
[_globalEventListeners addObject:monitor];
|
|
return monitor;
|
|
}
|
|
|
|
- (id)addEventMonitorForDeviceHandler:(OEDeviceHandler *)device handler:(void(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler;
|
|
{
|
|
NSAssert(device != nil, @"You must provide a device handler, use addUnhandledEventMonitorHandler: instead.");
|
|
NSHashTable<_OEDeviceManagerEventMonitor *> *monitors = _deviceHandlersToEventListeners[device];
|
|
if(monitors == nil) {
|
|
monitors = [NSHashTable weakObjectsHashTable];
|
|
_deviceHandlersToEventListeners[device] = monitors;
|
|
}
|
|
|
|
_OEDeviceManagerEventMonitor *monitor = [_OEDeviceManagerEventMonitor monitorWithEventMonitorHandler:handler];
|
|
[monitors addObject:monitor];
|
|
return monitor;
|
|
}
|
|
|
|
- (id)addUnhandledEventMonitorHandler:(void(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler;
|
|
{
|
|
_OEDeviceManagerEventMonitor *monitor = [_OEDeviceManagerEventMonitor monitorWithEventMonitorHandler:handler];
|
|
[_unhandledEventListeners addObject:monitor];
|
|
return monitor;
|
|
}
|
|
|
|
- (void)removeMonitor:(id)monitor;
|
|
{
|
|
[_globalEventListeners removeObject:monitor];
|
|
[_unhandledEventListeners removeObject:monitor];
|
|
|
|
NSMutableArray<OEDeviceHandler *> *keysToRemove = [NSMutableArray array];
|
|
[_deviceHandlersToEventListeners enumerateKeysAndObjectsUsingBlock:^(OEDeviceHandler *key, NSHashTable *monitors, BOOL *stop) {
|
|
[monitors removeObject:monitor];
|
|
|
|
if([monitors count] == 0)
|
|
[keysToRemove addObject:key];
|
|
}];
|
|
|
|
[_deviceHandlersToEventListeners removeObjectsForKeys:keysToRemove];
|
|
}
|
|
|
|
#pragma mark - Keyboard management
|
|
|
|
- (void)OE_addKeyboardEventMonitor;
|
|
{
|
|
_keyEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask | NSKeyUpMask handler:^ NSEvent * (NSEvent *anEvent) {
|
|
/* Events with a process ID of 0 comes from the system, that is from the physical keyboard.
|
|
* These events are already managed by their own device handler.
|
|
* The events managed through this monitor are events coming from different applications.
|
|
*/
|
|
if(CGEventGetIntegerValueField([anEvent CGEvent], kCGEventSourceUnixProcessID) == 0)
|
|
return anEvent;
|
|
|
|
OEHIDEvent *event = [OEHIDEvent keyEventWithTimestamp:[anEvent timestamp] keyCode:[OEHIDEvent keyCodeForVirtualKey:[anEvent keyCode]] state:[anEvent type] == NSKeyDown cookie:OEUndefinedCookie];
|
|
[[OEDeviceManager sharedDeviceManager] deviceHandler:nil didReceiveEvent:event];
|
|
|
|
return anEvent;
|
|
}];
|
|
|
|
_modifierMaskMonitor =
|
|
[NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:^ NSEvent * (NSEvent *anEvent) {
|
|
/* Events with a process ID of 0 comes from the system, that is from the physical keyboard.
|
|
* These events are already managed by their own device handler.
|
|
* The events managed through this monitor are events coming from different applications.
|
|
*/
|
|
if(CGEventGetIntegerValueField([anEvent CGEvent], kCGEventSourceUnixProcessID) == 0)
|
|
return anEvent;
|
|
|
|
NSUInteger keyCode = [OEHIDEvent keyCodeForVirtualKey:[anEvent keyCode]];
|
|
NSUInteger keyMask = 0;
|
|
|
|
switch(keyCode) {
|
|
case kHIDUsage_KeyboardCapsLock : keyMask = NSAlphaShiftKeyMask; break;
|
|
|
|
case kHIDUsage_KeyboardLeftControl : keyMask = 0x0001; break;
|
|
case kHIDUsage_KeyboardLeftShift : keyMask = 0x0002; break;
|
|
case kHIDUsage_KeyboardRightShift : keyMask = 0x0004; break;
|
|
case kHIDUsage_KeyboardLeftGUI : keyMask = 0x0008; break;
|
|
case kHIDUsage_KeyboardRightGUI : keyMask = 0x0010; break;
|
|
case kHIDUsage_KeyboardLeftAlt : keyMask = 0x0020; break;
|
|
case kHIDUsage_KeyboardRightAlt : keyMask = 0x0040; break;
|
|
case kHIDUsage_KeyboardRightControl : keyMask = 0x2000; break;
|
|
}
|
|
|
|
OEHIDEvent *event = [OEHIDEvent keyEventWithTimestamp:[anEvent timestamp] keyCode:keyCode state:!!([anEvent modifierFlags] & keyMask) cookie:OEUndefinedCookie];
|
|
[[OEDeviceManager sharedDeviceManager] deviceHandler:nil didReceiveEvent:event];
|
|
|
|
return anEvent;
|
|
}];
|
|
}
|
|
|
|
#pragma mark - IOHIDDevice management
|
|
|
|
- (void)OE_enumerateDevicesUsingBlock:(void(^)(OEDeviceHandler *device, BOOL *stop))block
|
|
{
|
|
BOOL stop = NO;
|
|
for(OEDeviceHandler *handler in _multiDeviceHandlers) {
|
|
block(handler, &stop);
|
|
if(stop)
|
|
return;
|
|
}
|
|
|
|
for(OEDeviceHandler *handler in _keyboardHandlers) {
|
|
block(handler, &stop);
|
|
if(stop)
|
|
return;
|
|
}
|
|
|
|
for(OEDeviceHandler *handler in _deviceHandlers) {
|
|
block(handler, &stop);
|
|
if(stop)
|
|
return;
|
|
}
|
|
}
|
|
|
|
- (void)OE_addDeviceHandlerForDeviceRef:(IOHIDDeviceRef)device
|
|
{
|
|
NSAssert(device != NULL, @"Passing NULL device.");
|
|
|
|
OEHIDDeviceHandler *handler = nil;
|
|
if(IOHIDDeviceConformsTo(device, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard))
|
|
handler = [[OEHIDDeviceHandler alloc] initWithIOHIDDevice:device deviceDescription:nil];
|
|
else
|
|
handler = [[OEHIDDeviceHandler deviceParser] deviceHandlerForIOHIDDevice:device];
|
|
|
|
if([handler connect])
|
|
[self OE_addDeviceHandler:handler];
|
|
}
|
|
|
|
- (void)OE_addDeviceHandler:(__kindof OEDeviceHandler *)handler
|
|
{
|
|
dispatch_barrier_async(_uniqueIdentifiersToDeviceHandlersQueue, ^{
|
|
OEDeviceHandlerPlaceholder *placeholder = _uniqueIdentifiersToDeviceHandlers[handler.uniqueIdentifier];
|
|
_uniqueIdentifiersToDeviceHandlers[handler.uniqueIdentifier] = handler;
|
|
|
|
if (![placeholder isKindOfClass:[OEDeviceHandlerPlaceholder class]])
|
|
return;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[placeholder notifyOriginalDeviceDidBecomeAvailable];
|
|
});
|
|
});
|
|
|
|
if([handler isKindOfClass:[OEMultiHIDDeviceHandler class]]) {
|
|
[handler setDeviceIdentifier:++_lastAttributedMultiDeviceIdentifier];
|
|
[_multiDeviceHandlers addObject:handler];
|
|
|
|
for(OEDeviceHandler *subhandler in [(OEMultiHIDDeviceHandler *)handler subdeviceHandlers])
|
|
[self OE_addDeviceHandler:subhandler];
|
|
|
|
return;
|
|
}
|
|
|
|
if([handler isKeyboardDevice]) {
|
|
[handler setDeviceIdentifier:++_lastAttributedKeyboardIdentifier];
|
|
[self willChangeValueForKey:@"keyboardDeviceHandlers"];
|
|
[_keyboardHandlers addObject:handler];
|
|
[self didChangeValueForKey:@"keyboardDeviceHandlers"];
|
|
} else {
|
|
NSAssert([[handler controllerDescription] numberOfControls] > 0, @"Handler %@ does not have any controls.", handler);
|
|
[handler setDeviceIdentifier:++_lastAttributedDeviceIdentifier];
|
|
[self willChangeValueForKey:@"controllerDeviceHandlers"];
|
|
[_deviceHandlers addObject:handler];
|
|
[self didChangeValueForKey:@"controllerDeviceHandlers"];
|
|
}
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:OEDeviceManagerDidAddDeviceHandlerNotification object:self userInfo:@{ OEDeviceManagerDeviceHandlerUserInfoKey : handler }];
|
|
}
|
|
|
|
- (BOOL)OE_hasDeviceHandlerForDeviceRef:(IOHIDDeviceRef)deviceRef
|
|
{
|
|
__block BOOL didFoundDevice = NO;
|
|
[self OE_enumerateDevicesUsingBlock:^(OEDeviceHandler *handler, BOOL *stop) {
|
|
if(![handler isKindOfClass:[OEHIDDeviceHandler class]] || [(OEHIDDeviceHandler *)handler device] != deviceRef)
|
|
return;
|
|
|
|
didFoundDevice = YES;
|
|
*stop = YES;
|
|
}];
|
|
|
|
return didFoundDevice;
|
|
}
|
|
|
|
- (void)OE_removeDeviceHandler:(__kindof OEDeviceHandler *)handler
|
|
{
|
|
dispatch_barrier_async(_uniqueIdentifiersToDeviceHandlersQueue, ^{
|
|
[_uniqueIdentifiersToDeviceHandlers removeObjectForKey:handler.uniqueIdentifier];
|
|
});
|
|
|
|
if([handler isKindOfClass:[OEMultiHIDDeviceHandler class]]) {
|
|
if(![_multiDeviceHandlers containsObject:handler])
|
|
return;
|
|
|
|
for(OEDeviceHandler *subhandler in [(OEMultiHIDDeviceHandler *)handler subdeviceHandlers])
|
|
[self OE_addDeviceHandler:subhandler];
|
|
|
|
[_multiDeviceHandlers removeObject:handler];
|
|
|
|
[(OEMultiHIDDeviceHandler *)handler disconnect];
|
|
|
|
return;
|
|
}
|
|
|
|
if([handler isKeyboardDevice]) {
|
|
if(![_keyboardHandlers containsObject:handler])
|
|
return;
|
|
|
|
[self willChangeValueForKey:@"keyboardDeviceHandlers"];
|
|
[_keyboardHandlers removeObject:handler];
|
|
[self didChangeValueForKey:@"keyboardDeviceHandlers"];
|
|
} else {
|
|
if(![_deviceHandlers containsObject:handler])
|
|
return;
|
|
|
|
[self willChangeValueForKey:@"controllerDeviceHandlers"];
|
|
[_deviceHandlers removeObject:handler];
|
|
[self didChangeValueForKey:@"controllerDeviceHandlers"];
|
|
}
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:OEDeviceManagerDidRemoveDeviceHandlerNotification object:self userInfo:@{ OEDeviceManagerDeviceHandlerUserInfoKey : handler }];
|
|
|
|
[(OEDeviceHandler *)handler disconnect];
|
|
}
|
|
|
|
- (void)OE_wiimoteDeviceDidDisconnect:(NSNotification *)notification
|
|
{
|
|
[self OE_removeDeviceHandler:[notification object]];
|
|
}
|
|
|
|
#pragma mark - Wiimote methods
|
|
|
|
- (BOOL)isBluetoothEnabled
|
|
{
|
|
BOOL powered = NO;
|
|
IOBluetoothHostController *controller = [IOBluetoothHostController defaultController];
|
|
|
|
if (controller != nil)
|
|
powered = ([controller powerState] == kBluetoothHCIPowerStateON);
|
|
|
|
return powered;
|
|
}
|
|
|
|
- (void)startWiimoteSearch;
|
|
{
|
|
@synchronized(self) {
|
|
//NSLog(@"Searching for Wiimotes");
|
|
|
|
_inquiry = [IOBluetoothDeviceInquiry inquiryWithDelegate:self];
|
|
[_inquiry setInquiryLength:3];
|
|
[_inquiry setUpdateNewDeviceNames:YES];
|
|
|
|
IOReturn status = [_inquiry start];
|
|
if(status == kIOReturnSuccess)
|
|
return;
|
|
|
|
[_inquiry setDelegate:nil];
|
|
_inquiry = nil;
|
|
NSLog(@"Error: Inquiry did not start, error %d", status);
|
|
}
|
|
}
|
|
|
|
- (void)stopWiimoteSearch;
|
|
{
|
|
@synchronized(self) {
|
|
[_inquiry stop];
|
|
[_inquiry setDelegate:nil];
|
|
_inquiry = nil;
|
|
}
|
|
}
|
|
|
|
#pragma mark - IOBluetoothDeviceInquiry Delegates
|
|
|
|
- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *)sender device:(IOBluetoothDevice *)device
|
|
{
|
|
//NSLog(@"%@ %@", NSStringFromSelector(_cmd), device);
|
|
// We do not stop the inquiry here because we want to find multiple Wii Remotes, and also because
|
|
// our search criteria is wide, and we may find non-Wiimotes.
|
|
}
|
|
|
|
- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry *)sender error:(IOReturn)error aborted:(BOOL)aborted
|
|
{
|
|
//NSLog(@"Devices: %@ Error: %d, Aborted: %s", [sender foundDevices], error, BOOL_STR(aborted));
|
|
|
|
[[sender foundDevices] enumerateObjectsUsingBlock:^(IOBluetoothDevice *obj, NSUInteger idx, BOOL *stop) {
|
|
// Check to make sure BT device name has Wiimote prefix. Note that there are multiple
|
|
// possible device names ("Nintendo RVL-CNT-01" and "Nintendo RVL-CNT-01-TR" at the
|
|
// time of writing), so we don't do an exact string match.
|
|
if(!OE_isWiimoteControllerName([obj name]))
|
|
return;
|
|
|
|
[obj openConnection];
|
|
if([obj isPaired])
|
|
return;
|
|
|
|
IOBluetoothDevicePair *pair = [IOBluetoothDevicePair pairWithDevice:obj];
|
|
[pair setDelegate:self];
|
|
[pair start];
|
|
}];
|
|
}
|
|
|
|
#pragma mark - IOBluetoothPairDelegate
|
|
|
|
- (void)devicePairingPINCodeRequest:(IOBluetoothDevicePair*)sender
|
|
{
|
|
NSLog(@"Attempting pair");
|
|
NSString *localAddress = [[[IOBluetoothHostController defaultController] addressAsString] uppercaseString];
|
|
NSString *remoteAddress = [[[sender device] addressString] uppercaseString];
|
|
|
|
BluetoothPINCode code;
|
|
NSScanner *scanner = [NSScanner scannerWithString:[sender attemptedHostToDevice]?localAddress:remoteAddress];
|
|
int byte = 5;
|
|
while(![scanner isAtEnd]) {
|
|
unsigned int data;
|
|
[scanner scanHexInt:&data];
|
|
code.data[byte] = data;
|
|
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"] intoString:nil];
|
|
byte--;
|
|
}
|
|
|
|
[sender replyPINCode:6 PINCode:&code];
|
|
}
|
|
|
|
- (void)devicePairingFinished:(IOBluetoothDevicePair*)sender error:(IOReturn)error
|
|
{
|
|
if(error == kIOReturnSuccess)
|
|
return;
|
|
|
|
if(![sender attemptedHostToDevice]) {
|
|
NSLog(@"Pairing failed, attempting inverse");
|
|
IOBluetoothDevicePair *pair = [IOBluetoothDevicePair pairWithDevice:[sender device]];
|
|
[[sender device] openConnection];
|
|
[pair setAttemptedHostToDevice:YES];
|
|
[pair setDelegate:self];
|
|
[pair start];
|
|
} else
|
|
NSLog(@"Couldn't pair, what gives?");
|
|
|
|
NSLog(@"Pairing finished %@: %x", sender, error);
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation _OEDeviceManagerEventMonitor
|
|
|
|
+ (instancetype)monitorWithGlobalMonitorHandler:(BOOL(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler
|
|
{
|
|
_OEDeviceManagerEventMonitor *monitor = [[self alloc] init];
|
|
[monitor setGlobalMonitor:handler];
|
|
return monitor;
|
|
}
|
|
|
|
+ (instancetype)monitorWithEventMonitorHandler:(void(^)(OEDeviceHandler *handler, OEHIDEvent *event))handler
|
|
{
|
|
_OEDeviceManagerEventMonitor *monitor = [[self alloc] init];
|
|
[monitor setEventMonitor:handler];
|
|
return monitor;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[[OEDeviceManager sharedDeviceManager] removeMonitor:self];
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark - HIDManager Callbacks
|
|
|
|
static void OEHandle_DeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef)
|
|
{
|
|
//NSLog(@"Found device: %s( context: %p, result: %#x, sender: %p, device: %p ).\n", __PRETTY_FUNCTION__, inContext, inResult, inSender, inIOHIDDeviceRef);
|
|
|
|
if([(__bridge OEDeviceManager *)inContext OE_hasDeviceHandlerForDeviceRef:inIOHIDDeviceRef]) {
|
|
NSLog(@"Device is already being handled");
|
|
return;
|
|
}
|
|
|
|
if(IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey)) == NULL) {
|
|
NSLog(@"Device does not have a location ID");
|
|
return;
|
|
}
|
|
|
|
if(IOHIDDeviceOpen(inIOHIDDeviceRef, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
|
|
NSLog(@"%s: failed to open device at %p", __PRETTY_FUNCTION__, inIOHIDDeviceRef);
|
|
return;
|
|
}
|
|
|
|
NSLog(@"%@", IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductKey)));
|
|
|
|
//add a OEHIDDeviceHandler for our HID device
|
|
[(__bridge OEDeviceManager *)inContext OE_addDeviceHandlerForDeviceRef:inIOHIDDeviceRef];
|
|
}
|
|
|
|
NS_ASSUME_NONNULL_END
|