From 257b94659d94527f5615a8447b2699218894847c Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Tue, 22 Nov 2011 20:37:49 +0100 Subject: [PATCH 1/6] 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 From d9e72fe73022cd2c10ea49ffdf17a45f56246262 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Mon, 21 Nov 2011 00:04:15 +0100 Subject: [PATCH 2/6] Change scope of propagate_event for use in io_mac.m -propagate_event was static in events.c -io_mac.m uses propagate_event when receiving data before finishing the handshake --- src/events.c | 3 +-- src/events.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/events.c b/src/events.c index 8666264..479ca12 100644 --- a/src/events.c +++ b/src/events.c @@ -59,7 +59,6 @@ static void idle_cycle(struct wiimote_t* wm); static void clear_dirty_reads(struct wiimote_t* wm); -static void propagate_event(struct wiimote_t* wm, byte event, byte* msg); static void event_data_read(struct wiimote_t* wm, byte* msg); static void event_data_write(struct wiimote_t *wm, byte *msg); static void event_status(struct wiimote_t* wm, byte* msg); @@ -305,7 +304,7 @@ static void clear_dirty_reads(struct wiimote_t* wm) { * * Pass the event to the registered event callback. */ -static void propagate_event(struct wiimote_t* wm, byte event, byte* msg) { +void propagate_event(struct wiimote_t* wm, byte event, byte* msg) { save_state(wm); switch (event) { diff --git a/src/events.h b/src/events.h index 1e42731..e9809e5 100644 --- a/src/events.h +++ b/src/events.h @@ -54,6 +54,8 @@ void wiiuse_pressed_buttons(struct wiimote_t* wm, byte* msg); void handshake_expansion(struct wiimote_t* wm, byte* data, uint16_t len); void disable_expansion(struct wiimote_t* wm); + +void propagate_event(struct wiimote_t* wm, byte event, byte* msg); /** @} */ #endif /* EVENTS_H_INCLUDED */ From e8126a1dca489a266cfa117a080dfb0227f50094 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Wed, 23 Nov 2011 00:02:26 +0100 Subject: [PATCH 3/6] Update headers on io_mac, wiiuse and wiiuse_internal -These are mostly copyright and consistency issues -On io_mac.h/m prepended "based on io_mac.h/m from WiiC," to "written by: Gabiele Randelli" and specified the files as part of wiiuse instead of wiiC. -On wiiuse.h and wiiuse_internal.h gave credit to WiiC and Gabiele Randelli for Mac-specific class codes and wiimote_t members --- src/io_mac.h | 4 ++-- src/io_mac.m | 4 ++-- src/wiiuse.h | 6 ++++++ src/wiiuse_internal.h | 6 ++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/io_mac.h b/src/io_mac.h index 906ab05..d5e2c6b 100755 --- a/src/io_mac.h +++ b/src/io_mac.h @@ -1,13 +1,13 @@ /* * io_mac.h * - * Written By: + * This file is based on io_mac.h from WiiC, written By: * Gabriele Randelli * Email: < randelli (--AT--) dis [--DOT--] uniroma1 [--DOT--] it > * * Copyright 2010 * - * This file is part of wiiC. + * This file is part of wiiuse. * * 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 diff --git a/src/io_mac.m b/src/io_mac.m index 30d994b..a586417 100755 --- a/src/io_mac.m +++ b/src/io_mac.m @@ -1,13 +1,13 @@ /* * io_mac.m * - * Written By: + * This file is based on io_mac.m from WiiC, written By: * Gabriele Randelli * Email: < randelli (--AT--) dis [--DOT--] uniroma1 [--DOT--] it > * * Copyright 2010 * - * This file is part of wiiC. + * This file is part of wiiuse. * * 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 diff --git a/src/wiiuse.h b/src/wiiuse.h index fb4bd07..59c162d 100644 --- a/src/wiiuse.h +++ b/src/wiiuse.h @@ -7,6 +7,12 @@ * * Copyright 2006-2007 * + * Mac fields based on wiic_structs.h from WiiC, written By: + * Gabriele Randelli + * Email: < randelli (--AT--) dis [--DOT--] uniroma1 [--DOT--] it > + * + * Copyright 2010 + * * This file is part of wiiuse. * * This program is free software; you can redistribute it and/or modify diff --git a/src/wiiuse_internal.h b/src/wiiuse_internal.h index 9bcc995..6aed90c 100644 --- a/src/wiiuse_internal.h +++ b/src/wiiuse_internal.h @@ -9,6 +9,12 @@ * * This file is part of wiiuse. * + * Mac device classes based on wiic_internal.h from WiiC, written By: + * Gabriele Randelli + * Email: < randelli (--AT--) dis [--DOT--] uniroma1 [--DOT--] it > + * + * Copyright 2010 + * * 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 From b208c940df6b33a7761fb65c8b708b806ba6a426 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Tue, 22 Nov 2011 21:24:38 +0100 Subject: [PATCH 4/6] example-sdl: include correct OpenGL and GLUT headers in Mac -OpenGL and GLUT Mac header paths differ from other platforms -ifdef-ed to include the correct headers depending on the platform --- example-sdl/sdl.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/example-sdl/sdl.c b/example-sdl/sdl.c index 1fddf32..1455fcd 100644 --- a/example-sdl/sdl.c +++ b/example-sdl/sdl.c @@ -28,9 +28,16 @@ #include /* for SDL_Event, SDL_PollEvent, etc */ #include /* for SDL_GL_SetAttribute, etc */ -#include /* for glVertex3f, GLfloat, etc */ -#include /* for gluLookAt, gluOrtho2D, etc */ -#include /* for glutSolidTeapot */ +#ifndef WIIUSE_MAC + #include /* for glVertex3f, GLfloat, etc */ + #include /* for gluLookAt, gluOrtho2D, etc */ + #include /* for glutSolidTeapot */ +#else + /* Mac doesn't use the same folders for OpenGL/GLUT includes */ + #include /* for glVertex3f, GLfloat, etc */ + #include /* for gluLookAt, gluOrtho2D, etc */ + #include /* for glutSolidTeapot */ +#endif #ifndef WIIUSE_WIN32 #include /* for timeval, gettimeofday */ From 87eba20df333bc65c3bd49858f0b2b885af7b8d1 Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Mon, 21 Nov 2011 02:04:31 +0100 Subject: [PATCH 5/6] README.mkd: Update contributors and add Mac usage -Added Gabriele Randelli from WiiC and Juan Sebastian Casallas to the contributor list -Added Mac OS X minimal requirements to "Platforms and Dependencies" -Added Mac to "Compiling" -Added an extra line for build to make sure you make the directory -Added known Mac-connection issues -Added Mac support to the list of WiiC's features --- README.mkd | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/README.mkd b/README.mkd index 767c693..81303fd 100644 --- a/README.mkd +++ b/README.mkd @@ -56,6 +56,8 @@ Additional Contributors: - admiral0 and fwiine project - Jeff Baker/Inv3rsion, LLC. - Jan Ciger - Reviatech SAS +- Gabriele Randelli and the WiiC project +- Juan Sebastian Casallas License @@ -102,6 +104,11 @@ Wiiuse currently operates on both Linux and Windows. You will need: - If compiling, Microsoft Windows Driver Development Kit (DDK) +### For Mac: ### + +- Mac OS X 10.2 or newer (to have the Mac OS X Bluetooth protocol stack) + + ### For either platform: ### - If compiling, [CMake](http://cmake.org) is needed to generate a makefile/project @@ -111,7 +118,9 @@ Compiling --------- You need SDL and OpenGL installed to compile the (optional) SDL example. -### Linux: ### +### Linux & Mac: ### + + $ mkdir build $ cd build @@ -176,7 +185,20 @@ If you are going to use Motion+, make sure to call wiiuse_poll or wiiuse_update in a loop for some 10-15 seconds before enabling it. Ideally you should be checking the status of any expansion (nunchuk) you may have connected as well. Otherwise the extra expansion may not initialize correctly - the initialization -and calibration takes some time. +and calibration takes some time. + + +### Mac OS X + +Sometimes you may not be able to connect to the device, even if it is discoverable. +If that happens open the Bluetooth Preferences, select the Nintendo device on the +list and remove it (by clicking on the minus sign). Close the Preference Pane and +try again. + +If you get the following error on runtime when connecting to your wiimote: +`Unable to write over the output channel`, +you probably won't be able to read IR or accelerometer data, or make the wiimote vibrate. +Relaunching your application and connecting again should solve this issue. Acknowledgements by Michael Laforest ------------------------------------ @@ -263,9 +285,11 @@ greatly appreciated. - [WiiC](http://wiic.sourceforge.net/) - - Dramatically changed, C++ API added + - Dramatically changed, C++ API added. - - MotionPlus support added + - MotionPlus support added. + + - Added Mac support. - DolphinEmu PPA: used to have a WiiUse 0.13 From 240ef2f3f966d14be509d4000861c88564775e9b Mon Sep 17 00:00:00 2001 From: Juan Sebastian Casallas Date: Wed, 30 Nov 2011 19:27:20 +0100 Subject: [PATCH 6/6] events.c: Clear event buffer after propagating in Mac -The line was previously commented-out -Even if the instruction is not vital, uncommenting it makes mac polling consistent with unix and windows polling --- src/events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events.c b/src/events.c index 479ca12..c26917f 100644 --- a/src/events.c +++ b/src/events.c @@ -206,7 +206,7 @@ int wiiuse_poll(struct wiimote_t** wm, int wiimotes) { evnt += (wm[i]->event != WIIUSE_NONE); /* clear out the event buffer */ - /*memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf));*/ + memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf)); } else { idle_cycle(wm[i]); }