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