Merge pull request #11 from jscasallas/wiic_mac

Mac support based on WiiC
This commit is contained in:
Ryan Pavlik
2011-11-30 14:30:21 -08:00
9 changed files with 1052 additions and 14 deletions

View File

@@ -55,6 +55,8 @@ Additional Contributors:
- admiral0 and fwiine project <http://sourceforge.net/projects/fwiine/files/wiiuse/0.13/>
- Jeff Baker/Inv3rsion, LLC. <http://www.inv3rsion.com/>
- Jan Ciger - Reviatech SAS <jan.ciger@reviatech.com>
- Gabriele Randelli and the WiiC project <http://wiic.sourceforge.net/>
- Juan Sebastian Casallas <https://github.com/jscasallas/wiiuse>
License
@@ -101,6 +103,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
@@ -110,7 +117,9 @@ Compiling
---------
You need SDL and OpenGL installed to compile the (optional) SDL example.
### Linux: ###
### Linux & Mac: ###
$ mkdir build
$ cd build
@@ -175,7 +184,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
------------------------------------
@@ -262,9 +284,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

View File

@@ -28,9 +28,16 @@
#include <SDL_events.h> /* for SDL_Event, SDL_PollEvent, etc */
#include <SDL_video.h> /* for SDL_GL_SetAttribute, etc */
#include <GL/gl.h> /* for glVertex3f, GLfloat, etc */
#include <GL/glu.h> /* for gluLookAt, gluOrtho2D, etc */
#include <GL/glut.h> /* for glutSolidTeapot */
#ifndef WIIUSE_MAC
#include <GL/gl.h> /* for glVertex3f, GLfloat, etc */
#include <GL/glu.h> /* for gluLookAt, gluOrtho2D, etc */
#include <GL/glut.h> /* for glutSolidTeapot */
#else
/* Mac doesn't use the same folders for OpenGL/GLUT includes */
#include <OpenGL/gl.h> /* for glVertex3f, GLfloat, etc */
#include <OpenGL/glu.h> /* for gluLookAt, gluOrtho2D, etc */
#include <GLUT/GLUT.h> /* for glutSolidTeapot */
#endif
#ifndef WIIUSE_WIN32
#include <sys/time.h> /* for timeval, gettimeofday */

View File

@@ -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

View File

@@ -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);
@@ -168,7 +167,7 @@ int wiiuse_poll(struct wiimote_t** wm, int wiimotes) {
idle_cycle(wm[i]);
}
}
#else
#elif defined(WIIUSE_WIN32)
/*
* Windows
*/
@@ -190,6 +189,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;
@@ -282,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) {

View File

@@ -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 */

107
src/io_mac.h Executable file
View File

@@ -0,0 +1,107 @@
/*
* io_mac.h
*
* 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 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
* 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 <http://www.gnu.org/licenses/>.
*
* $Header$
*
*/
/**
* @file
* @brief I/O header file for MacOS.
*/
#ifndef IO_MAC_H
#define IO_MAC_H
#import <stdio.h>
#import <stdlib.h>
#import <unistd.h>
#define BLUETOOTH_VERSION_USE_CURRENT
#import <arpa/inet.h> /* htons() */
#import <IOBluetooth/IOBluetoothUtilities.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#import <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
#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 */

790
src/io_mac.m Executable file
View File

@@ -0,0 +1,790 @@
/*
* io_mac.m
*
* 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 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
* 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 <http://www.gnu.org/licenses/>.
*
* $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: <HOME_DIR>/.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

View File

@@ -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
@@ -76,6 +82,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 +98,11 @@
/* nix */
#include <bluetooth/bluetooth.h>
#endif
#ifdef WIIUSE_MAC
/* mac */
#include <CoreFoundation/CoreFoundation.h> /*CFRunLoops and CFNumberRef in Bluetooth classes*/
#include <IOBluetooth/IOBluetoothUserLib.h> /*IOBluetoothDeviceRef and IOBluetoothL2CAPChannelRef*/
#endif
#ifndef WCONST
#define WCONST const
@@ -688,7 +702,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 +718,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 */

View File

@@ -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
@@ -51,6 +57,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 +72,11 @@
#include <arpa/inet.h> /* htons() */
#include <bluetooth/bluetooth.h>
#endif
#ifdef WIIUSE_MAC
/* mac */
#include <CoreFoundation/CoreFoundation.h> /*CFRunLoops and CFNumberRef in Bluetooth classes*/
#include <IOBluetooth/IOBluetoothUserLib.h> /*IOBluetoothDeviceRef and IOBluetoothL2CAPChannelRef*/
#endif
#include "definitions.h"
@@ -117,9 +131,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