From 257b94659d94527f5615a8447b2699218894847c Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Tue, 22 Nov 2011 20:37:49 +0100 Subject: [PATCH] Add io_mac files from wiic, update wiiuse definitions -Used io_mac.h/.m from wiic and added them to CMakeLists -Define WIIUSE_MAC on wiiuse.h and wiiuse_internal.h -Implemented wiiuse_poll when WIIUSE_MAC is defined -Added Mac-specific includes to wiiuse.h and wiiuse_internal.h -Added Mac-specific members to wiimote_t on wiiuse.h -Added Mac-specific WM_DEV_CLASS definitions to wiiuse_internal.h -Modified wiiuse_poll for use with Mac -Added init and cleanup functions to io_mac.m --- src/CMakeLists.txt | 16 + src/events.c | 25 +- src/io_mac.h | 107 ++++++ src/io_mac.m | 790 ++++++++++++++++++++++++++++++++++++++++++ src/wiiuse.h | 28 +- src/wiiuse_internal.h | 38 +- 6 files changed, 999 insertions(+), 5 deletions(-) create mode 100755 src/io_mac.h create mode 100755 src/io_mac.m diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6eb4ea7..95caf5d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,9 @@ set(API if(WIN32) list(APPEND SOURCES io_win.c) set(CMAKE_DEBUG_POSTFIX _debug) +elseif(APPLE) + list(APPEND SOURCES io_mac.h) + list(APPEND SOURCES io_mac.m) else() list(APPEND SOURCES io_nix.c) endif() @@ -44,6 +47,19 @@ if(WIN32) target_link_libraries(wiiuse ws2_32 setupapi ${WINHID_LIBRARIES}) elseif(LINUX) target_link_libraries(wiiuse m ${BLUEZ_LIBRARIES}) +elseif(APPLE) + find_library(IOBLUETOOTH_FRAMEWORK + NAMES + IOBluetooth) + + find_library(COREFOUNDATION_FRAMEWORK + NAMES + CoreFoundation) + + find_library(FOUNDATION_FRAMEWORK + NAMES + Foundation) + target_link_libraries(wiiuse ${IOBLUETOOTH_FRAMEWORK} ${COREFOUNDATION_FRAMEWORK} ${FOUNDATION_FRAMEWORK}) endif() set_property(TARGET diff --git a/src/events.c b/src/events.c index 61b1605..8666264 100644 --- a/src/events.c +++ b/src/events.c @@ -168,7 +168,7 @@ int wiiuse_poll(struct wiimote_t** wm, int wiimotes) { idle_cycle(wm[i]); } } - #else + #elif defined(WIIUSE_WIN32) /* * Windows */ @@ -190,6 +190,29 @@ int wiiuse_poll(struct wiimote_t** wm, int wiimotes) { idle_cycle(wm[i]); } } + #elif defined(WIIUSE_MAC) + /* + * Mac + */ + int i; + + if (!wm) return 0; + + for (i = 0; i < wiimotes; ++i) { + wm[i]->event = WIIUSE_NONE; + + if (wiiuse_io_read(wm[i])) { + /* propagate the event, messages should be read as in linux, starting from the second element */ + propagate_event(wm[i], wm[i]->event_buf[1], wm[i]->event_buf+2); + evnt += (wm[i]->event != WIIUSE_NONE); + + /* clear out the event buffer */ + /*memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf));*/ + } else { + idle_cycle(wm[i]); + } + } + #endif return evnt; diff --git a/src/io_mac.h b/src/io_mac.h new file mode 100755 index 0000000..906ab05 --- /dev/null +++ b/src/io_mac.h @@ -0,0 +1,107 @@ +/* + * io_mac.h + * + * Written By: + * Gabriele Randelli + * Email: < randelli (--AT--) dis [--DOT--] uniroma1 [--DOT--] it > + * + * Copyright 2010 + * + * This file is part of wiiC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header$ + * + */ + +/** + * @file + * @brief I/O header file for MacOS. + */ +#ifndef IO_MAC_H +#define IO_MAC_H + +#import +#import +#import + +#define BLUETOOTH_VERSION_USE_CURRENT + +#import /* htons() */ +#import +#import +#import +#import +#import + +#import "wiiuse_internal.h" +#import "io.h" + +@interface WiiSearch : NSObject +{ + IOBluetoothDeviceInquiry* inquiry; + BOOL isDiscovering; + // Number of found wiimotes + int foundWiimotes; + // Maximum number of wiimotes to be searched + int maxWiimotes; + // The Wiimotes structure + wiimote** wiimotes; +} + +- (BOOL) isDiscovering; +- (void) setDiscovering:(BOOL) flag; +- (void) setWiimoteStruct:(wiimote**) wiimote_struct; +- (int) getFoundWiimotes; +- (IOReturn) start:(unsigned int) timeout maxWiimotes:(unsigned int) wiimotesNum; +- (IOReturn) stop; +- (IOReturn) close; +- (void) retrieveWiimoteInfo:(IOBluetoothDevice*) device; +- (void) deviceInquiryStarted:(IOBluetoothDeviceInquiry*) sender; +- (void) deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *) sender device:(IOBluetoothDevice *) device; +- (void) deviceInquiryComplete:(IOBluetoothDeviceInquiry*) sender error:(IOReturn) error aborted:(BOOL) aborted; + +@end + +@interface WiiConnect : NSObject +{ + // Buffer to store incoming data from the Wiimote + NSData* receivedMsg; + unsigned int msgLength; + + // Reference to the relative wiimote struct (used only to complete handshaking) + wiimote* _wm; + BOOL isReading; + BOOL timeout; + BOOL disconnecting; +} + +- (IOBluetoothL2CAPChannel *) openL2CAPChannelWithPSM:(BluetoothL2CAPPSM) psm device:(IOBluetoothDevice*) device delegate:(id) delegate; +- (IOReturn) connectToWiimote:(wiimote*) wm; +- (void) l2capChannelData:(IOBluetoothL2CAPChannel*) channel data:(byte *) data length:(NSUInteger) length; +- (byte*) getNextMsg; +- (unsigned int) getMsgLength; +- (void) deleteMsg; +- (void) disconnected:(IOBluetoothUserNotification*) notification fromDevice:(IOBluetoothDevice*) device; +- (BOOL) isReading; +- (void) setReading:(BOOL) flag; +- (BOOL) isTimeout; +- (void) setTimeout:(BOOL) flag; +- (void) startTimerThread; +- (void) wakeUpMainThreadRunloop:(id)arg; +- (BOOL) isDisconnecting; +@end + +#endif /* IO_MAC_H */ diff --git a/src/io_mac.m b/src/io_mac.m new file mode 100755 index 0000000..30d994b --- /dev/null +++ b/src/io_mac.m @@ -0,0 +1,790 @@ +/* + * io_mac.m + * + * Written By: + * Gabriele Randelli + * Email: < randelli (--AT--) dis [--DOT--] uniroma1 [--DOT--] it > + * + * Copyright 2010 + * + * This file is part of wiiC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header$ + * + */ + +/** + * @file + * @brief Handles device I/O for Mac. + */ +#ifdef __APPLE__ + +#import "io_mac.h" +#import "events.h" + +@implementation WiiSearch + +#pragma mark - +#pragma mark WiiSearch +- (id) init +{ + self = [super init]; + foundWiimotes = 0; + isDiscovering = NO; + if (self != nil) { + /* + * Calling IOBluetoothLocalDeviceAvailable has two advantages: + * 1. it sets up a event source in the run loop (bug for C version of the bluetooth api) + * 2. it checks for the availability of the BT hardware + */ + if (![IOBluetoothHostController defaultController]) + { + [self release]; + self = nil; + } + } + + return self; +} + +- (void) dealloc +{ + inquiry = 0; + WIIUSE_DEBUG("Wiimote Discovery released"); + [super dealloc]; +} + +- (BOOL) isDiscovering +{ + return isDiscovering; +} + +- (void) setDiscovering:(BOOL) flag +{ + isDiscovering = flag; +} + +- (void) setWiimoteStruct:(wiimote**) wiimote_struct +{ + wiimotes = wiimote_struct; +} + +- (int) getFoundWiimotes +{ + return foundWiimotes; +} + +- (IOReturn) start:(unsigned int) timeout maxWiimotes:(unsigned int) wiimotesNum +{ + if (![IOBluetoothHostController defaultController]) { + WIIUSE_ERROR("Unable to find any bluetooth receiver on your host."); + return kIOReturnNotAttached; + } + + // If we are currently discovering, we can't start a new discovery right now. + if ([self isDiscovering]) { + WIIUSE_INFO("Wiimote search is already in progress..."); + return kIOReturnSuccess; + } + + [self close]; + maxWiimotes = wiimotesNum; + foundWiimotes = 0; + + inquiry = [IOBluetoothDeviceInquiry inquiryWithDelegate:self]; + // We set the search timeout + if(timeout == 0) + [inquiry setInquiryLength:5]; + else if(timeout < 20) + [inquiry setInquiryLength:timeout]; + else + [inquiry setInquiryLength:20]; + [inquiry setSearchCriteria:kBluetoothServiceClassMajorAny majorDeviceClass:WM_DEV_MAJOR_CLASS minorDeviceClass:WM_DEV_MINOR_CLASS]; + [inquiry setUpdateNewDeviceNames:NO]; + + IOReturn status = [inquiry start]; + if (status == kIOReturnSuccess) { + [inquiry retain]; + } else { + [self close]; + WIIUSE_ERROR("Unable to search for bluetooth devices."); + } + + return status; +} + +- (IOReturn) stop +{ + return [inquiry stop]; +} + +- (IOReturn) close +{ + IOReturn ret = kIOReturnSuccess; + + ret = [inquiry stop]; + [inquiry release]; + inquiry = nil; + + WIIUSE_DEBUG("Discovery closed"); + return ret; +} + +#pragma mark - +#pragma mark IOBluetoothDeviceInquiry delegates +//*************** HANDLERS FOR WIIUSE_FIND FOR MACOSX *******************/ +- (void) retrieveWiimoteInfo:(IOBluetoothDevice*) device +{ + // We set the device reference (we must retain it to use it after the search) + wiimotes[foundWiimotes]->device = [[device retain] getDeviceRef]; + wiimotes[foundWiimotes]->address = (CFStringRef)[[device getAddressString] retain]; + + // C String (common for Mac and Linux) + CFStringGetCString(wiimotes[foundWiimotes]->address,wiimotes[foundWiimotes]->bdaddr_str,18,kCFStringEncodingMacRoman); + + WIIMOTE_ENABLE_STATE(wiimotes[foundWiimotes], WIIMOTE_STATE_DEV_FOUND); + WIIUSE_INFO("Found Wiimote (%s) [id %i].",CFStringGetCStringPtr(wiimotes[foundWiimotes]->address, kCFStringEncodingMacRoman),wiimotes[foundWiimotes]->unid); + ++foundWiimotes; +} + +- (void) deviceInquiryStarted:(IOBluetoothDeviceInquiry*) sender +{ + [self setDiscovering:YES]; +} + +- (void) deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *) sender device:(IOBluetoothDevice *) device +{ + if(foundWiimotes < maxWiimotes) + [self retrieveWiimoteInfo:device]; + else + [inquiry stop]; +} + +- (void) deviceInquiryComplete:(IOBluetoothDeviceInquiry*) sender error:(IOReturn) error aborted:(BOOL) aborted +{ + // The inquiry has completed, we can now process what we have found + [self setDiscovering:NO]; + + // We stop the search because of errors + if ((error != kIOReturnSuccess) && !aborted) { + foundWiimotes = 0; + [self close]; + WIIUSE_ERROR("Search not completed, because of unexpected errors. This error can be due to a short search timeout."); + return; + } + + foundWiimotes = [[inquiry foundDevices] count]; +} + +@end + +@implementation WiiConnect +#pragma mark - +#pragma mark WiiConnect +- (id) init +{ + self = [super init]; + receivedMsg = [[NSData alloc] init]; + msgLength = 0; + _wm = 0; + isReading = NO; + timeout = NO; + disconnecting = NO; + return self; +} + +- (void) dealloc +{ + WIIUSE_DEBUG("Wiimote released"); + if(receivedMsg) + [receivedMsg release]; + receivedMsg = 0; + _wm = 0; + [super dealloc]; +} + +- (byte*) getNextMsg +{ + if(!receivedMsg) + return 0; + + return (byte*)[receivedMsg bytes]; +} + +- (void) deleteMsg +{ + if(receivedMsg) { + [receivedMsg release]; + msgLength = 0; + } +} + +- (BOOL) isDisconnecting +{ + return disconnecting; +} + +- (BOOL) isReading +{ + return isReading; +} + +- (void) setReading:(BOOL) flag +{ + isReading = flag; +} + +- (BOOL) isTimeout +{ + return timeout; +} + +- (void) setTimeout:(BOOL) flag +{ + timeout = flag; +} + +- (void) startTimerThread +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // Timer + sleep(1); + + [pool drain]; +} + +- (void) wakeUpMainThreadRunloop:(id)arg +{ + // This method is executed on main thread! + // It doesn't need to do anything actually, just having it run will + // make sure the main thread stops running the runloop +} + + +- (IOBluetoothL2CAPChannel*) openL2CAPChannelWithPSM:(BluetoothL2CAPPSM) psm device:(IOBluetoothDevice*) device delegate:(id) delegate +{ + IOBluetoothL2CAPChannel* channel = nil; + + if ([device openL2CAPChannelSync:&channel withPSM:psm delegate:delegate] != kIOReturnSuccess) + channel = nil; + + return channel; +} + +- (IOReturn) connectToWiimote:(wiimote*) wm +{ + IOBluetoothDevice* device = [IOBluetoothDevice withDeviceRef:wm->device]; + IOBluetoothL2CAPChannel* outCh = nil; + IOBluetoothL2CAPChannel* inCh = nil; + + if(!device) { + WIIUSE_ERROR("Non existent device or already connected."); + return kIOReturnBadArgument; + } + + outCh = [self openL2CAPChannelWithPSM:WM_OUTPUT_CHANNEL device:device delegate:self]; + if (!outCh) { + WIIUSE_ERROR("Unable to open L2CAP output channel (id %i).", wm->unid); + [device closeConnection]; + return kIOReturnNotOpen; + } + wm->outputCh = [[outCh retain] getL2CAPChannelRef]; + usleep(20000); + + inCh = [self openL2CAPChannelWithPSM:WM_INPUT_CHANNEL device:device delegate:self]; + if (!inCh) { + WIIUSE_ERROR("Unable to open L2CAP input channel (id %i).", wm->unid); + [device closeConnection]; + return kIOReturnNotOpen; + } + wm->inputCh = [[inCh retain] getL2CAPChannelRef]; + usleep(20000); + + IOBluetoothUserNotification* disconnectNotification = [device registerForDisconnectNotification:self selector:@selector(disconnected:fromDevice:)]; + if(!disconnectNotification) { + WIIUSE_ERROR("Unable to register disconnection handler (id %i).", wm->unid); + [device closeConnection]; + return kIOReturnNotOpen; + } + + // We store the reference to its relative structure (Used for completing the handshake step) + _wm = wm; + + return kIOReturnSuccess; +} + +#pragma mark - +#pragma mark IOBluetoothL2CAPChannel delegates +- (void) disconnected:(IOBluetoothUserNotification*) notification fromDevice:(IOBluetoothDevice*) device +{ + [self deleteMsg]; + [self setReading:NO]; + disconnecting = YES; + + // The wiimote_t struct must be re-initialized due to the disconnection + wiiuse_disconnected(_wm) ; +} + +//*************** HANDLERS FOR WIIUSE_IO_READ FOR MACOSX *******************/ +- (void) l2capChannelData:(IOBluetoothL2CAPChannel*) channel data:(byte *) data length:(NSUInteger) length +{ + // This is done in case the output channel woke up this handler + if(!data) { + [self setReading:NO]; + length = 0; + return; + } + + /* + * This is called if we are receiving data before completing + * the handshaking, hence before calling wiiuse_poll + */ + if(WIIMOTE_IS_SET(_wm, WIIMOTE_STATE_HANDSHAKE)) + propagate_event(_wm, data[1], data+2); + + receivedMsg = [[NSData dataWithBytes:data length:length] retain]; + msgLength = length; + + // This is done when a message is successfully received. Stop the main loop after reading + [self setReading:NO]; +} + +- (unsigned int) getMsgLength +{ + return msgLength; +} + +- (void) l2capChannelReconfigured:(IOBluetoothL2CAPChannel*) l2capChannel +{ + //NSLog(@"l2capChannelReconfigured"); +} + +- (void) l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*) l2capChannel refcon:(void*) refcon status:(IOReturn) error +{ + //NSLog(@"l2capChannelWriteComplete"); +} + +- (void) l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*) l2capChannel +{ + //NSLog(@"l2capChannelQueueSpaceAvailable"); +} + +- (void) l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*) l2capChannel status:(IOReturn) error +{ + //NSLog(@"l2capChannelOpenComplete (PSM:0x%x)", [l2capChannel getPSM]); +} + + +@end + +#pragma mark - +#pragma mark Wiiuse +/** + * @brief Find a wiimote or wiimotes. + * + * @param wm An array of wiimote_t structures. + * @param max_wiimotes The number of wiimote structures in \a wm. + * @param timeout The number of seconds before the search times out. + * + * @return The number of wiimotes found. + * + * @see wiimote_connect() + * + * This function will only look for wiimote devices. \n + * When a device is found the address in the structures will be set. \n + * You can then call wiimote_connect() to connect to the found \n + * devices. + * + * This function is defined in wiiuse.h + */ +int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout) +{ + int found_wiimotes = 0; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + WiiSearch* search = [[WiiSearch alloc] init]; + [search setWiimoteStruct:wm]; + + if(timeout) { // Single search + [search start:timeout maxWiimotes:max_wiimotes]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while ([search isDiscovering] && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + found_wiimotes = [search getFoundWiimotes]; + } + else { // Unlimited search + found_wiimotes = 0; + while(!found_wiimotes) { + [search start:timeout maxWiimotes:max_wiimotes]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while ([search isDiscovering] && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + found_wiimotes = [search getFoundWiimotes]; + } + } + + WIIUSE_INFO("Found %i Wiimote device(s).", found_wiimotes); + + [search release]; + [pool drain]; + + return found_wiimotes; +} + + +//*************** HANDLERS FOR WIIUSE_DISCONNECT FOR MACOSX *******************/ +/** + * @brief Disconnect a wiimote. + * + * @param wm Pointer to a wiimote_t structure. + * + * @see wiic_connect() + * + * Note that this will not free the wiimote structure. + * + * This function is defined in wiiuse.h + */ +void wiiuse_disconnect(struct wiimote_t* wm) +{ + IOReturn error; + + if (!wm || !WIIMOTE_IS_CONNECTED(wm)) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Input Channel + if(wm->inputCh) { + IOBluetoothL2CAPChannel* inCh = [IOBluetoothL2CAPChannel withL2CAPChannelRef:wm->inputCh]; + error = [inCh closeChannel]; + [inCh setDelegate:nil]; + if(error != kIOReturnSuccess) + WIIUSE_ERROR("Unable to close input channel (id %i).", wm->unid); + usleep(10000); + [inCh release]; + inCh = nil; + wm->inputCh = 0; + } + + // Output Channel + if(wm->outputCh) { + IOBluetoothL2CAPChannel* outCh = [IOBluetoothL2CAPChannel withL2CAPChannelRef:wm->outputCh]; + error = [outCh closeChannel]; + [outCh setDelegate:nil]; + if(error != kIOReturnSuccess) + WIIUSE_ERROR("Unable to close output channel (id %i).", wm->unid); + usleep(10000); + [outCh release]; + outCh = nil; + wm->outputCh = 0; + } + + // Device + if(wm->device) { + IOBluetoothDevice* device = [IOBluetoothDevice withDeviceRef:wm->device]; + error = [device closeConnection]; + + if(error != kIOReturnSuccess) + WIIUSE_ERROR("Unable to close the device connection (id %i).", wm->unid); + usleep(10000); + [device release]; + device = nil; + wm->device = 0; + } + + [pool drain]; + + wm->event = WIIUSE_NONE; + + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED); + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE); +} + +/** + * @brief Connect to a wiimote with a known address. + * + * @param wm Pointer to a wiimote_t structure. + * @param address The address of the device to connect to. + * If NULL, use the address in the struct set by wiic_find(). + * + * @return 1 on success, 0 on failure + * This function is defined in io_mac.h + */ +static int wiiuse_connect_single(struct wiimote_t* wm, char* address) +{ + // Skip if already connected or device not found + if(!wm || WIIMOTE_IS_CONNECTED(wm) || wm->device == 0) { + WIIUSE_ERROR("Non existent device or already connected."); + return 0; + } + + // Convert the IP address + // FIXME - see if it is possible + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + WiiConnect* connect = [[[WiiConnect alloc] init] autorelease]; + if([connect connectToWiimote:wm] == kIOReturnSuccess) { + WIIUSE_INFO("Connected to wiimote [id %i].", wm->unid); + // This is stored to retrieve incoming data + wm->connectionHandler = (void*)([connect retain]); + + // Do the handshake + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_CONNECTED); + wiiuse_handshake(wm, NULL, 0); + wiiuse_set_report_type(wm); + + [pool drain]; + } + else { + [pool drain]; + return 0; + } + + return 1; +} + +/** + * @brief Connect to a wiimote or wiimotes once an address is known. + * + * @param wm An array of wiimote_t structures. + * @param wiimotes The number of wiimote structures in \a wm. + * + * @return The number of wiimotes that successfully connected. + * + * @see wiic_find() + * @see wiic_connect_single() + * @see wiic_disconnect() + * + * Connect to a number of wiimotes when the address is already set + * in the wiimote_t structures. These addresses are normally set + * by the wiic_find() function, but can also be set manually. + * This function is defined in wiiuse.h + */ +int wiiuse_connect(struct wiimote_t** wm, int wiimotes) +{ + int connected = 0; + int i = 0; + + for (; i < wiimotes; ++i) { + if(!(wm[i])) { + WIIUSE_ERROR("Trying to connect more Wiimotes than initialized"); + return 0; + } + + if (!WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_DEV_FOUND)) + // If the device address is not set, skip it + continue; + + if (wiiuse_connect_single(wm[i], NULL)) + ++connected; + } + + return connected; +} + +/** + * @brief Load Wii devices registered in the wiimotes.config file. + * + * @param wm An array of wiimote_t structures. + * + * @return The number of wiimotes successfully loaded. + * + * @see wiic_find() + * @see wiic_connect() + * @see wiic_connect_single() + * @see wiic_disconnect() + * + * From version 0.53, it is possible to register the MAC address of your + * Wii devices. This allows to automatically load them, without waiting for any + * search timeout. To register a new device, go to: /.wiic/ and + * edit the file wiimotes.config, by adding the MAC address of the device + * you want to register (one line per MAC address). + * This function seems useful but it's not implemented in wiiuse + * TODO: Erase or implement elsewhere + */ +int wiiuse_load(struct wiimote_t** wm) +{ + int loaded = 0; + int i = 0; + char str[200]; + char configPath[100]; + char* tmp = 0; + + // Retrieve the HOME environment variable + tmp = getenv("HOME"); + strcpy(configPath,tmp); + strncat(configPath,"/.wiic/wiimotes.config",22); + + // Open the config file + FILE* fd = 0; + fd = fopen(configPath,"r"); + if(!fd) + return loaded; + + // Read line by line + while(fgets(str,sizeof(str),fd) != NULL && loaded < 1) { + int len = strlen(str)-1; + if(str[len] == '\n') + str[len] = 0; + loaded++; + } + + // We initialize the device structure + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + for (; i < loaded; ++i) { + NSString* string = [NSString stringWithCString:str encoding:[NSString defaultCStringEncoding]]; + BluetoothDeviceAddress deviceAddr; + IOBluetoothNSStringToDeviceAddress(string, &deviceAddr); + IOBluetoothDevice* device = [IOBluetoothDevice withAddress:&deviceAddr]; + wm[i]->device = [[device retain] getDeviceRef]; + wm[i]->address = (CFStringRef)[[device getAddressString] retain]; + WIIMOTE_ENABLE_STATE(wm[i], WIIMOTE_STATE_DEV_FOUND); + WIIUSE_INFO("Loaded Wiimote (%s) [id %i].",CFStringGetCStringPtr(wm[i]->address, kCFStringEncodingMacRoman),wm[i]->unid); + } + [pool drain]; + + return loaded; +} + +// Defined in io.h +int wiiuse_io_read(struct wiimote_t* wm) +{ + if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_CONNECTED)) + return 0; + + /* If this wiimote is not connected, skip it */ + if (!WIIMOTE_IS_CONNECTED(wm)) + return 0; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + WiiConnect* deviceHandler = 0; + deviceHandler = (WiiConnect*)(wm->connectionHandler); + + /* If this wiimote is disconnecting, skip it */ + if (!deviceHandler || [deviceHandler isDisconnecting]) { + [pool drain]; + return 0; + } + + // Run the main loop to get bt data + [deviceHandler setReading:YES]; + [deviceHandler setTimeout:NO]; + + // We start the thread which manages the timeout to implement a non-blocking read + [NSThread detachNewThreadSelector:@selector(startTimerThread) toTarget:deviceHandler withObject:nil]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Two possible events: we receive and incoming message or there is a timeout + while([deviceHandler isReading] && ![deviceHandler isTimeout]) { + NSAutoreleasePool *pool_loop = [[NSAutoreleasePool alloc] init]; // This is used for fast release of NSDate, otherwise it leaks + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + [pool_loop drain]; + } + + // In this case we have no incoming data (TIMEOUT) + if([deviceHandler isTimeout]) { + [pool drain]; + return 0; + } + + if(!(wm->connectionHandler)) { + WIIUSE_ERROR("Unable to find the connection handler (id %i).", wm->unid); + [pool drain]; + return 0; + } + + // Read next message + byte* buffer = 0; + unsigned int length = 0; + if(![deviceHandler isDisconnecting]) { + buffer = [deviceHandler getNextMsg]; + length = [deviceHandler getMsgLength]; + } + + if(!buffer || !length) { + [pool drain]; + return 0; + } + + // Forward to WiiC + if(length < sizeof(wm->event_buf)) + memcpy(wm->event_buf,buffer,length); + else { + WIIUSE_DEBUG("Received data are more than the buffer.... strange! (id %i)", wm->unid); + memcpy(wm->event_buf,buffer,sizeof(wm->event_buf)); + } + + // Release the consumed message + [deviceHandler deleteMsg]; + + [pool drain]; + + return 1; +} + +// Defined in io.h +int wiiuse_io_write(struct wiimote_t* wm, byte* buf, int len) +{ + unsigned int length = (unsigned int)len; + + // Small check before writing + if(!wm || !(wm->outputCh)) { + WIIUSE_ERROR("Attempt to write over non-existent channel (id %i).", wm->unid); + perror("Error Details"); + return 0; + } + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + IOBluetoothL2CAPChannel* channel = [IOBluetoothL2CAPChannel withL2CAPChannelRef:wm->outputCh]; + IOReturn error = [channel writeSync:buf length:length]; + if (error != kIOReturnSuccess) + WIIUSE_ERROR("Unable to write over the output channel (id %i).", wm->unid); + usleep(10000); + + [pool drain]; + + return (error == kIOReturnSuccess ? len : 0); +} + +void wiiuse_init_platform_fields(struct wiimote_t* wm) +{ + wm->device = nil; + wm->address = nil; + wm->inputCh = nil; + wm->outputCh = 0; + wm->disconnectionRef = nil; + wm->connectionHandler = NULL; + memset (wm->bdaddr_str,'\0',18); +} + +void wiiuse_cleanup_platform_fields(struct wiimote_t* wm) +{ + /* TODO isn't this already done in wiiuse_disconnect ? */ + wm->device = nil; + wm->address = nil; + wm->inputCh = nil; + wm->outputCh = 0; + wm->disconnectionRef = nil; + wm->connectionHandler = NULL; + memset (wm->bdaddr_str,'\0',18); +} + +#endif diff --git a/src/wiiuse.h b/src/wiiuse.h index ce11f88..fb4bd07 100644 --- a/src/wiiuse.h +++ b/src/wiiuse.h @@ -76,6 +76,9 @@ #elif defined(__linux) #define WIIUSE_PLATFORM #define WIIUSE_BLUEZ + #elif defined(__APPLE__) + #define WIIUSE_PLATFORM + #define WIIUSE_MAC #else #error "Platform not yet supported!" #endif @@ -89,6 +92,11 @@ /* nix */ #include #endif +#ifdef WIIUSE_MAC + /* mac */ + #include /*CFRunLoops and CFNumberRef in Bluetooth classes*/ + #include /*IOBluetoothDeviceRef and IOBluetoothL2CAPChannelRef*/ +#endif #ifndef WCONST #define WCONST const @@ -688,7 +696,6 @@ typedef struct wiimote_t { /** @name Linux-specific (BlueZ) members */ /** @{ */ WCONST bdaddr_t bdaddr; /**< bt address */ - WCONST char bdaddr_str[18]; /**< readable bt address */ WCONST int out_sock; /**< output socket */ WCONST int in_sock; /**< input socket */ /** @} */ @@ -705,6 +712,25 @@ typedef struct wiimote_t { WCONST byte exp_timeout; /**< timeout for expansion handshake */ /** @} */ #endif + + #ifdef WIIUSE_MAC + /** @name Mac OS X-specific members */ + /** @{ */ + WCONST IOBluetoothDeviceRef device; /** Device reference object */ + WCONST CFStringRef address; /** MacOS-like device address string */ + WCONST IOBluetoothL2CAPChannelRef inputCh; /** Input L2CAP channel */ + WCONST IOBluetoothL2CAPChannelRef outputCh; /** Output L2CAP channel */ + WCONST IOBluetoothUserNotificationRef disconnectionRef; /** Disconnection Notification Reference **/ + WCONST void* connectionHandler; /** Wiimote connection handler for MACOSX **/ + /** @} */ + #endif + + #if defined(WIIUSE_BLUEZ) || defined(WIIUSE_MAC) + /** @name Linux (BlueZ) and Mac OS X shared members */ + /** @{ */ + WCONST char bdaddr_str[18]; /**< readable bt address */ + /** @} */ + #endif WCONST int state; /**< various state flags */ WCONST byte leds; /**< currently lit leds */ diff --git a/src/wiiuse_internal.h b/src/wiiuse_internal.h index f244b8b..9bcc995 100644 --- a/src/wiiuse_internal.h +++ b/src/wiiuse_internal.h @@ -51,6 +51,9 @@ #elif defined(__linux) #define WIIUSE_PLATFORM #define WIIUSE_BLUEZ + #elif defined(__APPLE__) + #define WIIUSE_PLATFORM + #define WIIUSE_MAC #else #error "Platform not yet supported!" #endif @@ -63,6 +66,11 @@ #include /* htons() */ #include #endif +#ifdef WIIUSE_MAC + /* mac */ + #include /*CFRunLoops and CFNumberRef in Bluetooth classes*/ + #include /*IOBluetoothDeviceRef and IOBluetoothL2CAPChannelRef*/ +#endif #include "definitions.h" @@ -117,9 +125,33 @@ #define WM_BT_OUTPUT 0x02 /* Identify the wiimote device by its class */ -#define WM_DEV_CLASS_0 0x04 -#define WM_DEV_CLASS_1 0x25 -#define WM_DEV_CLASS_2 0x00 + +/* (Explanation and mac classes taken from WiiC) + * The different codes wrt. to Linux + * is a bit hard to explain. + * Looking at Bluetooth CoD format, we have 24 bits. + * In wiic Linux they are stored in three fields, + * each one 8bit long. The result number is + * 0x002504. However, MacOS Bluetooth does + * not store them in such a way, rather it uses + * the concept of major service, major class, + * and minor class, that are respectivelly + * 11bit, 5bit, and 6bit long. Hence, the + * numbers are different. + * The Wiimote CoD Bluetooth division is the following: + * 00000000001 00101 000001 00 (major service - major class - minor class - format type) + * This can also be seen in the WiiC Linux way: + * 00000000 00100101 00000100 + */ +#ifdef WIIUSE_MAC + #define WM_DEV_MINOR_CLASS 0x01 + #define WM_DEV_MAJOR_CLASS 0x05 + #define WM_DEV_MAJOR_SERVICE 0x01 +#else + #define WM_DEV_CLASS_0 0x04 + #define WM_DEV_CLASS_1 0x25 + #define WM_DEV_CLASS_2 0x00 +#endif #define WM_VENDOR_ID 0x057E #define WM_PRODUCT_ID 0x0306