Organise source files towards gradle project layout

This commit is contained in:
2014-11-25 22:56:31 +00:00
parent 73cca98ccc
commit d3eae9a2e1
32 changed files with 2 additions and 1 deletions

175
src/main/c/classic.c Normal file
View File

@@ -0,0 +1,175 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Classic controller expansion device.
*/
#include "classic.h"
#include "dynamics.h" /* for calc_joystick_state */
#include "events.h" /* for handshake_expansion */
#include <stdlib.h> /* for malloc */
#include <string.h> /* for memset */
static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now);
/**
* @brief Handle the handshake data from the classic controller.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* @return Returns 1 if handshake was successful, 0 if not.
*/
#define HANDSHAKE_BYTES_USED 12
int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, byte* data, unsigned short len) {
cc->btns = 0;
cc->btns_held = 0;
cc->btns_released = 0;
cc->r_shoulder = 0;
cc->l_shoulder = 0;
if (data[0] == 0xFF || len < HANDSHAKE_BYTES_USED) {
/*
* Sometimes the data returned here is not correct.
* This might happen because the wiimote is lagging
* behind our initialization sequence.
* To fix this just request the handshake again.
*
* Other times it's just the first 16 bytes are 0xFF,
* but since the next 16 bytes are the same, just use
* those.
*/
if (len < 17 || len < HANDSHAKE_BYTES_USED + 16 || data[16] == 0xFF) {
/* get the calibration data */
byte* handshake_buf = (byte *)malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
WIIUSE_DEBUG("Classic controller handshake appears invalid, trying again.");
wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
return 0;
} else {
data += 16;
}
}
/* joystick stuff */
cc->ljs.max.x = data[0] / 4;
cc->ljs.min.x = data[1] / 4;
cc->ljs.center.x = data[2] / 4;
cc->ljs.max.y = data[3] / 4;
cc->ljs.min.y = data[4] / 4;
cc->ljs.center.y = data[5] / 4;
cc->rjs.max.x = data[6] / 8;
cc->rjs.min.x = data[7] / 8;
cc->rjs.center.x = data[8] / 8;
cc->rjs.max.y = data[9] / 8;
cc->rjs.min.y = data[10] / 8;
cc->rjs.center.y = data[11] / 8;
/* handshake done */
wm->exp.type = EXP_CLASSIC;
#ifdef WIIUSE_WIN32
wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
#endif
return 1;
}
/**
* @brief The classic controller disconnected.
*
* @param cc A pointer to a classic_ctrl_t structure.
*/
void classic_ctrl_disconnected(struct classic_ctrl_t* cc) {
memset(cc, 0, sizeof(struct classic_ctrl_t));
}
/**
* @brief Handle classic controller event.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param msg The message specified in the event packet.
*/
void classic_ctrl_event(struct classic_ctrl_t* cc, byte* msg) {
int lx, ly, rx, ry;
byte l, r;
classic_ctrl_pressed_buttons(cc, from_big_endian_uint16_t(msg + 4));
/* left/right buttons */
l = (((msg[2] & 0x60) >> 2) | ((msg[3] & 0xE0) >> 5));
r = (msg[3] & 0x1F);
/*
* TODO - LR range hardcoded from 0x00 to 0x1F.
* This is probably in the calibration somewhere.
*/
cc->r_shoulder = ((float)r / 0x1F);
cc->l_shoulder = ((float)l / 0x1F);
/* calculate joystick orientation */
lx = (msg[0] & 0x3F);
ly = (msg[1] & 0x3F);
rx = ((msg[0] & 0xC0) >> 3) | ((msg[1] & 0xC0) >> 5) | ((msg[2] & 0x80) >> 7);
ry = (msg[2] & 0x1F);
calc_joystick_state(&cc->ljs, (float)lx, (float)ly);
calc_joystick_state(&cc->rjs, (float)rx, (float)ry);
}
/**
* @brief Find what buttons are pressed.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param msg The message byte specified in the event packet.
*/
static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now) {
/* message is inverted (0 is active, 1 is inactive) */
now = ~now & CLASSIC_CTRL_BUTTON_ALL;
/* pressed now & were pressed, then held */
cc->btns_held = (now & cc->btns);
/* were pressed or were held & not pressed now, then released */
cc->btns_released = ((cc->btns | cc->btns_held) & ~now);
/* buttons pressed now */
cc->btns = now;
}

229
src/main/c/dynamics.c Normal file
View File

@@ -0,0 +1,229 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 the dynamics of the wiimote.
*
* The file includes functions that handle the dynamics
* of the wiimote. Such dynamics include orientation and
* motion sensing.
*/
#include "dynamics.h"
#include <math.h> /* for atan2f, atanf, sqrt */
#include <stdlib.h> /* for abs */
/**
* @brief Calculate the roll, pitch, yaw.
*
* @param ac An accelerometer (accel_t) structure.
* @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data.
* @param orient [out] Pointer to a orient_t structure that will hold the orientation data.
* @param rorient [out] Pointer to a orient_t structure that will hold the non-smoothed orientation data.
* @param smooth If smoothing should be performed on the angles calculated. 1 to enable, 0 to disable.
*
* Given the raw acceleration data from the accelerometer struct, calculate
* the orientation of the device and set it in the \a orient parameter.
*/
void calculate_orientation(struct accel_t* ac, struct vec3b_t* accel, struct orient_t* orient, int smooth) {
float xg, yg, zg;
float x, y, z;
/*
* roll - use atan(z / x) [ ranges from -180 to 180 ]
* pitch - use atan(z / y) [ ranges from -180 to 180 ]
* yaw - impossible to tell without IR
*/
/* yaw - set to 0, IR will take care of it if it's enabled */
orient->yaw = 0.0f;
/* find out how much it has to move to be 1g */
xg = (float)ac->cal_g.x;
yg = (float)ac->cal_g.y;
zg = (float)ac->cal_g.z;
/* find out how much it actually moved and normalize to +/- 1g */
x = ((float)accel->x - (float)ac->cal_zero.x) / xg;
y = ((float)accel->y - (float)ac->cal_zero.y) / yg;
z = ((float)accel->z - (float)ac->cal_zero.z) / zg;
/* make sure x,y,z are between -1 and 1 for the tan functions */
if (x < -1.0f) {
x = -1.0f;
} else if (x > 1.0f) {
x = 1.0f;
}
if (y < -1.0f) {
y = -1.0f;
} else if (y > 1.0f) {
y = 1.0f;
}
if (z < -1.0f) {
z = -1.0f;
} else if (z > 1.0f) {
z = 1.0f;
}
/* if it is over 1g then it is probably accelerating and not reliable */
if (abs(accel->x - ac->cal_zero.x) <= ac->cal_g.x) {
/* roll */
x = RAD_TO_DEGREE(atan2f(x, z));
orient->roll = x;
orient->a_roll = x;
}
if (abs(accel->y - ac->cal_zero.y) <= ac->cal_g.y) {
/* pitch */
y = RAD_TO_DEGREE(atan2f(y, z));
orient->pitch = y;
orient->a_pitch = y;
}
/* smooth the angles if enabled */
if (smooth) {
apply_smoothing(ac, orient, SMOOTH_ROLL);
apply_smoothing(ac, orient, SMOOTH_PITCH);
}
}
/**
* @brief Calculate the gravity forces on each axis.
*
* @param ac An accelerometer (accel_t) structure.
* @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data.
* @param gforce [out] Pointer to a gforce_t structure that will hold the gravity force data.
*/
void calculate_gforce(struct accel_t* ac, struct vec3b_t* accel, struct gforce_t* gforce) {
float xg, yg, zg;
/* find out how much it has to move to be 1g */
xg = (float)ac->cal_g.x;
yg = (float)ac->cal_g.y;
zg = (float)ac->cal_g.z;
/* find out how much it actually moved and normalize to +/- 1g */
gforce->x = ((float)accel->x - (float)ac->cal_zero.x) / xg;
gforce->y = ((float)accel->y - (float)ac->cal_zero.y) / yg;
gforce->z = ((float)accel->z - (float)ac->cal_zero.z) / zg;
}
static float applyCalibration(float inval, float minval, float maxval, float centerval) {
float ret;
/* We don't use the exact ranges but the ranges + 1 in case we get bad calibration data - avoid div0 */
if (inval == centerval) {
ret = 0;
} else if (inval < centerval) {
ret = (inval - minval) / (centerval - minval + 1.0f) - 1.0f;
} else {
ret = (inval - centerval) / (maxval - centerval + 1.0f);
}
return ret;
}
/**
* @brief Calculate the angle and magnitude of a joystick.
*
* @param js [out] Pointer to a joystick_t structure.
* @param x The raw x-axis value.
* @param y The raw y-axis value.
*/
void calc_joystick_state(struct joystick_t* js, float x, float y) {
float rx, ry, ang;
/*
* Since the joystick center may not be exactly:
* (min + max) / 2
* Then the range from the min to the center and the center to the max
* may be different.
* Because of this, depending on if the current x or y value is greater
* or less than the associated axis center value, it needs to be interpolated
* between the center and the minimum or maxmimum rather than between
* the minimum and maximum.
*
* So we have something like this:
* (x min) [-1] ---------*------ [0] (x center) [0] -------- [1] (x max)
* Where the * is the current x value.
* The range is therefore -1 to 1, 0 being the exact center rather than
* the middle of min and max.
*/
rx = applyCalibration(x, js->min.x, js->max.x, js->center.x);
ry = applyCalibration(y, js->min.y, js->max.y, js->center.y);
js->x = rx;
js->y = ry;
/* calculate the joystick angle and magnitude */
ang = RAD_TO_DEGREE(atan2f(ry, rx));
js->ang = ang + 180.0f;
js->mag = sqrtf((rx * rx) + (ry * ry));
}
void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type) {
switch (type) {
case SMOOTH_ROLL: {
/* it's possible last iteration was nan or inf, so set it to 0 if that happened */
if (isnan(ac->st_roll) || isinf(ac->st_roll)) {
ac->st_roll = 0.0f;
}
/*
* If the sign changes (which will happen if going from -180 to 180)
* or from (-1 to 1) then don't smooth, just use the new angle.
*/
if (((ac->st_roll < 0) && (orient->roll > 0)) || ((ac->st_roll > 0) && (orient->roll < 0))) {
ac->st_roll = orient->roll;
} else {
orient->roll = ac->st_roll + (ac->st_alpha * (orient->a_roll - ac->st_roll));
ac->st_roll = orient->roll;
}
return;
}
case SMOOTH_PITCH: {
if (isnan(ac->st_pitch) || isinf(ac->st_pitch)) {
ac->st_pitch = 0.0f;
}
if (((ac->st_pitch < 0) && (orient->pitch > 0)) || ((ac->st_pitch > 0) && (orient->pitch < 0))) {
ac->st_pitch = orient->pitch;
} else {
orient->pitch = ac->st_pitch + (ac->st_alpha * (orient->a_pitch - ac->st_pitch));
ac->st_pitch = orient->pitch;
}
return;
}
}
}

991
src/main/c/events.c Normal file
View File

@@ -0,0 +1,991 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 wiimote events.
*
* The file includes functions that handle the events
* that are sent from the wiimote to us.
*/
#include "wiiuse_internal.h"
#include "events.h"
#include "classic.h" /* for classic_ctrl_disconnected, etc */
#include "dynamics.h" /* for calculate_gforce, etc */
#include "guitar_hero_3.h" /* for guitar_hero_3_disconnected, etc */
#include "ir.h" /* for calculate_basic_ir, etc */
#include "nunchuk.h" /* for nunchuk_disconnected, etc */
#include "wiiboard.h" /* for wii_board_disconnected, etc */
#include "motion_plus.h" /* for motion_plus_disconnected, etc */
#include "os.h" /* for wiiuse_os_poll */
#include <stdio.h> /* for printf, perror */
#include <stdlib.h> /* for free, malloc */
#include <string.h> /* for memcpy, memset */
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);
static void handle_expansion(struct wiimote_t* wm, byte* msg);
static void save_state(struct wiimote_t* wm);
static int state_changed(struct wiimote_t* wm);
/**
* @brief Poll the wiimotes for any events.
*
* @param wm An array of pointers to wiimote_t structures.
* @param wiimotes The number of wiimote_t structures in the \a wm array.
*
* @return Returns number of wiimotes that an event has occurred on.
*
* It is necessary to poll the wiimote devices for events
* that occur. If an event occurs on a particular wiimote,
* the event variable will be set.
*/
int wiiuse_poll(struct wiimote_t** wm, int wiimotes) {
return wiiuse_os_poll(wm, wiimotes);
}
int wiiuse_update(struct wiimote_t** wiimotes, int nwiimotes, wiiuse_update_cb callback) {
int evnt = 0;
if (wiiuse_poll(wiimotes, nwiimotes)) {
static struct wiimote_callback_data_t s;
int i = 0;
for (; i < nwiimotes; ++i) {
switch (wiimotes[i]->event) {
case WIIUSE_NONE:
break;
default:
/* this could be: WIIUSE_EVENT, WIIUSE_STATUS, WIIUSE_CONNECT, etc.. */
s.uid = wiimotes[i]->unid;
s.leds = wiimotes[i]->leds;
s.battery_level = wiimotes[i]->battery_level;
s.accel = wiimotes[i]->accel;
s.orient = wiimotes[i]->orient;
s.gforce = wiimotes[i]->gforce;
s.ir = wiimotes[i]->ir;
s.buttons = wiimotes[i]->btns;
s.buttons_held = wiimotes[i]->btns_held;
s.buttons_released = wiimotes[i]->btns_released;
s.event = wiimotes[i]->event;
s.state = wiimotes[i]->state;
s.expansion = wiimotes[i]->exp;
callback(&s);
evnt++;
break;
}
}
}
return evnt;
}
/**
* @brief Called on a cycle where no significant change occurs.
*
* @param wm Pointer to a wiimote_t structure.
*/
void idle_cycle(struct wiimote_t* wm) {
/*
* Smooth the angles.
*
* This is done to make sure that on every cycle the orientation
* angles are smoothed. Normally when an event occurs the angles
* are updated and smoothed, but if no packet comes in then the
* angles remain the same. This means the angle wiiuse reports
* is still an old value. Smoothing needs to be applied in this
* case in order for the angle it reports to converge to the true
* angle of the device.
*/
if (WIIUSE_USING_ACC(wm) && WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)) {
apply_smoothing(&wm->accel_calib, &wm->orient, SMOOTH_ROLL);
apply_smoothing(&wm->accel_calib, &wm->orient, SMOOTH_PITCH);
}
/* clear out any old read requests */
clear_dirty_reads(wm);
}
/**
* @brief Clear out all old 'dirty' read requests.
*
* @param wm Pointer to a wiimote_t structure.
*/
void clear_dirty_reads(struct wiimote_t* wm) {
struct read_req_t* req = wm->read_req;
while (req && req->dirty) {
WIIUSE_DEBUG("Cleared old read request for address: %x", req->addr);
wm->read_req = req->next;
free(req);
req = wm->read_req;
}
}
/**
* @brief Handle accel data in a wiimote message.
*
* @param wm Pointer to a wiimote_t structure.
* @param msg The message specified in the event packet.
*/
static void handle_wm_accel(struct wiimote_t* wm, byte* msg) {
wm->accel.x = msg[2];
wm->accel.y = msg[3];
wm->accel.z = msg[4];
/* calculate the remote orientation */
calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING));
/* calculate the gforces on each axis */
calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce);
}
/**
* @brief Analyze the event that occurred on a wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param event The event that occurred.
* @param msg The message specified in the event packet.
*
* Pass the event to the registered event callback.
*/
void propagate_event(struct wiimote_t* wm, byte event, byte* msg) {
save_state(wm);
switch (event) {
case WM_RPT_BTN: {
/* button */
wiiuse_pressed_buttons(wm, msg);
break;
}
case WM_RPT_BTN_ACC: {
/* button - motion */
wiiuse_pressed_buttons(wm, msg);
handle_wm_accel(wm, msg);
break;
}
case WM_RPT_READ: {
/* data read */
event_data_read(wm, msg);
/* yeah buttons may be pressed, but this wasn't an "event" */
return;
}
case WM_RPT_CTRL_STATUS: {
/* controller status */
event_status(wm, msg);
/* don't execute the event callback */
return;
}
case WM_RPT_BTN_EXP: {
/* button - expansion */
wiiuse_pressed_buttons(wm, msg);
handle_expansion(wm, msg + 2);
break;
}
case WM_RPT_BTN_ACC_EXP: {
/* button - motion - expansion */
wiiuse_pressed_buttons(wm, msg);
handle_wm_accel(wm, msg);
handle_expansion(wm, msg + 5);
break;
}
case WM_RPT_BTN_ACC_IR: {
/* button - motion - ir */
wiiuse_pressed_buttons(wm, msg);
handle_wm_accel(wm, msg);
/* ir */
calculate_extended_ir(wm, msg + 5);
break;
}
case WM_RPT_BTN_IR_EXP: {
/* button - ir - expansion */
wiiuse_pressed_buttons(wm, msg);
handle_expansion(wm, msg + 12);
/* ir */
calculate_basic_ir(wm, msg + 2);
break;
}
case WM_RPT_BTN_ACC_IR_EXP: {
/* button - motion - ir - expansion */
wiiuse_pressed_buttons(wm, msg);
handle_wm_accel(wm, msg);
handle_expansion(wm, msg + 15);
/* ir */
calculate_basic_ir(wm, msg + 5);
break;
}
case WM_RPT_WRITE: {
event_data_write(wm, msg);
break;
}
default: {
WIIUSE_WARNING("Unknown event, can not handle it [Code 0x%x].", event);
return;
}
}
/* was there an event? */
if (state_changed(wm)) {
wm->event = WIIUSE_EVENT;
}
}
/**
* @brief Find what buttons are pressed.
*
* @param wm Pointer to a wiimote_t structure.
* @param msg The message specified in the event packet.
*/
void wiiuse_pressed_buttons(struct wiimote_t* wm, byte* msg) {
int16_t now;
/* convert from big endian */
now = from_big_endian_uint16_t(msg) & WIIMOTE_BUTTON_ALL;
/* pressed now & were pressed, then held */
wm->btns_held = (now & wm->btns);
/* were pressed or were held & not pressed now, then released */
wm->btns_released = ((wm->btns | wm->btns_held) & ~now);
/* buttons pressed now */
wm->btns = now;
}
/**
* @brief Received a data packet from a read request.
*
* @param wm Pointer to a wiimote_t structure.
* @param msg The message specified in the event packet.
*
* Data from the wiimote comes in packets. If the requested
* data segment size is bigger than one packet can hold then
* several packets will be received. These packets are first
* reassembled into one, then the registered callback function
* that handles data reads is invoked.
*/
static void event_data_read(struct wiimote_t* wm, byte* msg) {
/* we must always assume the packet received is from the most recent request */
byte err;
byte len;
uint16_t offset;
struct read_req_t* req = wm->read_req;
wiiuse_pressed_buttons(wm, msg);
/* find the next non-dirty request */
while (req && req->dirty) {
req = req->next;
}
/* if we don't have a request out then we didn't ask for this packet */
if (!req) {
WIIUSE_WARNING("Received data packet when no request was made.");
return;
}
err = msg[2] & 0x0F;
if (err == 0x08) {
WIIUSE_WARNING("Unable to read data - address does not exist.");
} else if (err == 0x07) {
WIIUSE_WARNING("Unable to read data - address is for write-only registers.");
} else if (err) {
WIIUSE_WARNING("Unable to read data - unknown error code %x.", err);
}
if (err) {
/* this request errored out, so skip it and go to the next one */
/* delete this request */
wm->read_req = req->next;
free(req);
/* if another request exists send it to the wiimote */
if (wm->read_req) {
wiiuse_send_next_pending_read_request(wm);
}
return;
}
len = ((msg[2] & 0xF0) >> 4) + 1;
offset = from_big_endian_uint16_t(msg + 3);
req->addr = (req->addr & 0xFFFF);
req->wait -= len;
if (req->wait >= req->size)
/* this should never happen */
{
req->wait = 0;
}
WIIUSE_DEBUG("Received read packet:");
WIIUSE_DEBUG(" Packet read offset: %i bytes", offset);
WIIUSE_DEBUG(" Request read offset: %i bytes", req->addr);
WIIUSE_DEBUG(" Read offset into buf: %i bytes", offset - req->addr);
WIIUSE_DEBUG(" Read data size: %i bytes", len);
WIIUSE_DEBUG(" Still need: %i bytes", req->wait);
/* reconstruct this part of the data */
memcpy((req->buf + offset - req->addr), (msg + 5), len);
#ifdef WITH_WIIUSE_DEBUG
{
int i = 0;
printf("Read: ");
for (; i < req->size - req->wait; ++i) {
printf("%x ", req->buf[i]);
}
printf("\n");
}
#endif
/* if all data has been received, execute the read event callback or generate event */
if (!req->wait) {
if (req->cb) {
/* this was a callback, so invoke it now */
req->cb(wm, req->buf, req->size);
/* delete this request */
wm->read_req = req->next;
free(req);
} else {
/*
* This should generate an event.
* We need to leave the event in the array so the client
* can access it still. We'll flag is as being 'dirty'
* and give the client one cycle to use it. Next event
* we will remove it from the list.
*/
wm->event = WIIUSE_READ_DATA;
req->dirty = 1;
}
/* if another request exists send it to the wiimote */
if (wm->read_req) {
wiiuse_send_next_pending_read_request(wm);
}
}
}
static void event_data_write(struct wiimote_t *wm, byte *msg) {
struct data_req_t* req = wm->data_req;
wiiuse_pressed_buttons(wm, msg);
/* if we don't have a request out then we didn't ask for this packet */
if (!req) {
WIIUSE_WARNING("Transmitting data packet when no request was made.");
return;
}
if (!(req->state == REQ_SENT)) {
WIIUSE_WARNING("Transmission is not necessary");
/* delete this request */
wm->data_req = req->next;
free(req);
return;
}
req->state = REQ_DONE;
if (req->cb) {
/* this was a callback, so invoke it now */
req->cb(wm, NULL, 0);
/* delete this request */
wm->data_req = req->next;
free(req);
} else {
/*
* This should generate an event.
* We need to leave the event in the array so the client
* can access it still. We'll flag is as being 'REQ_DONE'
* and give the client one cycle to use it. Next event
* we will remove it from the list.
*/
wm->event = WIIUSE_WRITE_DATA;
}
/* if another request exists send it to the wiimote */
if (wm->data_req) {
wiiuse_send_next_pending_write_request(wm);
}
}
/**
* @brief Read the controller status.
*
* @param wm Pointer to a wiimote_t structure.
* @param msg The message specified in the event packet.
*
* Read the controller status and execute the registered status callback.
*/
static void event_status(struct wiimote_t* wm, byte* msg) {
int led[4] = {0, 0, 0, 0};
int attachment = 0;
int ir = 0;
int exp_changed = 0;
struct data_req_t* req = wm->data_req;
/* initial handshake is not finished yet, ignore this */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_HANDSHAKE) || !msg) {
return;
}
/*
* An event occurred.
* This event can be overwritten by a more specific
* event type during a handshake or expansion removal.
*/
wm->event = WIIUSE_STATUS;
wiiuse_pressed_buttons(wm, msg);
/* find what LEDs are lit */
if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_1) {
led[0] = 1;
}
if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_2) {
led[1] = 1;
}
if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_3) {
led[2] = 1;
}
if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_4) {
led[3] = 1;
}
/* probe for Motion+ */
if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_MPLUS_PRESENT)) {
wiiuse_probe_motion_plus(wm);
}
/* is an attachment connected to the expansion port? */
if ((msg[2] & WM_CTRL_STATUS_BYTE1_ATTACHMENT) == WM_CTRL_STATUS_BYTE1_ATTACHMENT) {
WIIUSE_DEBUG("Attachment detected!");
attachment = 1;
}
/* is the speaker enabled? */
if ((msg[2] & WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED) == WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED) {
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_SPEAKER);
}
/* is IR sensing enabled? */
if ((msg[2] & WM_CTRL_STATUS_BYTE1_IR_ENABLED) == WM_CTRL_STATUS_BYTE1_IR_ENABLED) {
ir = 1;
}
/* find the battery level and normalize between 0 and 1 */
wm->battery_level = (msg[5] / (float)WM_MAX_BATTERY_CODE);
/* expansion port */
if (attachment && !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP) && !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP_HANDSHAKE)) {
/* send the initialization code for the attachment */
handshake_expansion(wm, NULL, 0);
exp_changed = 1;
} else if (!attachment && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
/* attachment removed */
disable_expansion(wm);
exp_changed = 1;
}
#ifdef WIIUSE_WIN32
if (!attachment) {
WIIUSE_DEBUG("Setting timeout to normal %i ms.", wm->normal_timeout);
wm->timeout = wm->normal_timeout;
}
#endif
/*
* From now on the remote will only send status packets.
* We need to send a WIIMOTE_CMD_REPORT_TYPE packet to
* reenable other incoming reports.
*/
if (exp_changed && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
/*
* Since the expansion status changed IR needs to
* be reset for the new IR report mode.
*/
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
wiiuse_set_ir(wm, 1);
} else {
wiiuse_set_report_type(wm);
return;
}
/* handling new Tx for changed exp */
if (!req) {
return;
}
if (!(req->state == REQ_SENT)) {
return;
}
wm->data_req = req->next;
req->state = REQ_DONE;
/* if(req->cb!=NULL) req->cb(wm,msg,6); */
free(req);
}
/**
* @brief Handle data from the expansion.
*
* @param wm A pointer to a wiimote_t structure.
* @param msg The message specified in the event packet for the expansion.
*/
static void handle_expansion(struct wiimote_t* wm, byte* msg) {
switch (wm->exp.type) {
case EXP_NUNCHUK:
nunchuk_event(&wm->exp.nunchuk, msg);
break;
case EXP_CLASSIC:
classic_ctrl_event(&wm->exp.classic, msg);
break;
case EXP_GUITAR_HERO_3:
guitar_hero_3_event(&wm->exp.gh3, msg);
break;
case EXP_WII_BOARD:
wii_board_event(&wm->exp.wb, msg);
break;
case EXP_MOTION_PLUS:
case EXP_MOTION_PLUS_CLASSIC:
case EXP_MOTION_PLUS_NUNCHUK:
motion_plus_event(&wm->exp.mp, wm->exp.type, msg);
break;
default:
break;
}
}
/**
* @brief Handle the handshake data from the expansion device.
*
* @param wm A pointer to a wiimote_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* Tries to determine what kind of expansion was attached
* and invoke the correct handshake function.
*
* If the data is NULL then this function will try to start
* a handshake with the expansion.
*/
void handshake_expansion(struct wiimote_t* wm, byte* data, uint16_t len) {
int id;
byte val = 0;
byte buf = 0x00;
byte* handshake_buf;
int gotIt = 0;
WIIUSE_DEBUG("handshake_expansion with state %d", wm->expansion_state);
switch (wm->expansion_state) {
/* These two initialization writes disable the encryption */
case 0:
wm->expansion_state = 1;
/* increase the timeout until the handshake completes */
#ifdef WIIUSE_WIN32
WIIUSE_DEBUG("write 0x55 - Setting timeout to expansion %i ms.", wm->exp_timeout);
wm->timeout = wm->exp_timeout;
#endif
buf = 0x55;
wiiuse_write_data_cb(wm, WM_EXP_MEM_ENABLE1, &buf, 1, handshake_expansion);
break;
case 1:
wm->expansion_state = 2;
/* increase the timeout until the handshake completes */
#ifdef WIIUSE_WIN32
WIIUSE_DEBUG("write 0x00 - Setting timeout to expansion %i ms.", wm->exp_timeout);
wm->timeout = wm->exp_timeout;
#endif
val = 0x00;
wiiuse_write_data_cb(wm, WM_EXP_MEM_ENABLE2, &buf, 1, handshake_expansion);
break;
case 2:
wm->expansion_state = 3;
/* get the calibration data */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
disable_expansion(wm);
}
handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
/* tell the wiimote to send expansion data */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP);
wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
break;
case 3:
if (!data || !len) {
WIIUSE_DEBUG("no handshake data received from expansion");
disable_expansion(wm);
return;
}
wm->expansion_state = 0;
id = from_big_endian_uint32_t(data + 220);
switch (id) {
case EXP_ID_CODE_NUNCHUK:
if (nunchuk_handshake(wm, &wm->exp.nunchuk, data, len)) {
wm->event = WIIUSE_NUNCHUK_INSERTED;
gotIt = 1;
}
break;
case EXP_ID_CODE_CLASSIC_CONTROLLER:
if (classic_ctrl_handshake(wm, &wm->exp.classic, data, len)) {
wm->event = WIIUSE_CLASSIC_CTRL_INSERTED;
gotIt = 1;
}
break;
case EXP_ID_CODE_GUITAR:
if (guitar_hero_3_handshake(wm, &wm->exp.gh3, data, len)) {
wm->event = WIIUSE_GUITAR_HERO_3_CTRL_INSERTED;
gotIt = 1;
}
break;
case EXP_ID_CODE_MOTION_PLUS:
case EXP_ID_CODE_MOTION_PLUS_CLASSIC:
case EXP_ID_CODE_MOTION_PLUS_NUNCHUK:
/* wiiuse_motion_plus_handshake(wm, data, len); */
wm->event = WIIUSE_MOTION_PLUS_ACTIVATED;
gotIt = 1;
break;
case EXP_ID_CODE_WII_BOARD:
if (wii_board_handshake(wm, &wm->exp.wb, data, len)) {
wm->event = WIIUSE_WII_BOARD_CTRL_INSERTED;
gotIt = 1;
}
break;
default:
WIIUSE_WARNING("Unknown expansion type. Code: 0x%x", id);
break;
}
free(data);
if (gotIt) {
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP);
} else {
WIIUSE_WARNING("Could not handshake with expansion id: 0x%x", id);
}
wiiuse_set_ir_mode(wm);
wiiuse_status(wm);
break;
}
}
/**
* @brief Disable the expansion device if it was enabled.
*
* @param wm A pointer to a wiimote_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* If the data is NULL then this function will try to start
* a handshake with the expansion.
*/
void disable_expansion(struct wiimote_t* wm) {
WIIUSE_DEBUG("Disabling expansion");
if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
return;
}
/* tell the associated module the expansion was removed */
switch (wm->exp.type) {
case EXP_NUNCHUK:
nunchuk_disconnected(&wm->exp.nunchuk);
wm->event = WIIUSE_NUNCHUK_REMOVED;
break;
case EXP_CLASSIC:
classic_ctrl_disconnected(&wm->exp.classic);
wm->event = WIIUSE_CLASSIC_CTRL_REMOVED;
break;
case EXP_GUITAR_HERO_3:
guitar_hero_3_disconnected(&wm->exp.gh3);
wm->event = WIIUSE_GUITAR_HERO_3_CTRL_REMOVED;
break;
case EXP_WII_BOARD:
wii_board_disconnected(&wm->exp.wb);
wm->event = WIIUSE_WII_BOARD_CTRL_REMOVED;
break;
case EXP_MOTION_PLUS:
case EXP_MOTION_PLUS_CLASSIC:
case EXP_MOTION_PLUS_NUNCHUK:
motion_plus_disconnected(&wm->exp.mp);
wm->event = WIIUSE_MOTION_PLUS_REMOVED;
break;
default:
break;
}
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
wm->exp.type = EXP_NONE;
wm->expansion_state = 0;
}
/**
* @brief Save important state data.
* @param wm A pointer to a wiimote_t structure.
*/
static void save_state(struct wiimote_t* wm) {
/* wiimote */
wm->lstate.btns = wm->btns;
wm->lstate.accel = wm->accel;
/* ir */
if (WIIUSE_USING_IR(wm)) {
wm->lstate.ir_ax = wm->ir.ax;
wm->lstate.ir_ay = wm->ir.ay;
wm->lstate.ir_distance = wm->ir.distance;
}
/* expansion */
switch (wm->exp.type) {
case EXP_NUNCHUK:
wm->lstate.exp_ljs_ang = wm->exp.nunchuk.js.ang;
wm->lstate.exp_ljs_mag = wm->exp.nunchuk.js.mag;
wm->lstate.exp_btns = wm->exp.nunchuk.btns;
wm->lstate.exp_accel = wm->exp.nunchuk.accel;
break;
case EXP_CLASSIC:
wm->lstate.exp_ljs_ang = wm->exp.classic.ljs.ang;
wm->lstate.exp_ljs_mag = wm->exp.classic.ljs.mag;
wm->lstate.exp_rjs_ang = wm->exp.classic.rjs.ang;
wm->lstate.exp_rjs_mag = wm->exp.classic.rjs.mag;
wm->lstate.exp_r_shoulder = wm->exp.classic.r_shoulder;
wm->lstate.exp_l_shoulder = wm->exp.classic.l_shoulder;
wm->lstate.exp_btns = wm->exp.classic.btns;
break;
case EXP_GUITAR_HERO_3:
wm->lstate.exp_ljs_ang = wm->exp.gh3.js.ang;
wm->lstate.exp_ljs_mag = wm->exp.gh3.js.mag;
wm->lstate.exp_r_shoulder = wm->exp.gh3.whammy_bar;
wm->lstate.exp_btns = wm->exp.gh3.btns;
break;
case EXP_WII_BOARD:
wm->lstate.exp_wb_rtr = wm->exp.wb.rtr;
wm->lstate.exp_wb_rtl = wm->exp.wb.rtl;
wm->lstate.exp_wb_rbr = wm->exp.wb.rbr;
wm->lstate.exp_wb_rbl = wm->exp.wb.rbl;
break;
case EXP_MOTION_PLUS:
case EXP_MOTION_PLUS_CLASSIC:
case EXP_MOTION_PLUS_NUNCHUK: {
wm->lstate.drx = wm->exp.mp.raw_gyro.pitch;
wm->lstate.dry = wm->exp.mp.raw_gyro.roll;
wm->lstate.drz = wm->exp.mp.raw_gyro.yaw;
if (wm->exp.type == EXP_MOTION_PLUS_CLASSIC) {
wm->lstate.exp_ljs_ang = wm->exp.classic.ljs.ang;
wm->lstate.exp_ljs_mag = wm->exp.classic.ljs.mag;
wm->lstate.exp_rjs_ang = wm->exp.classic.rjs.ang;
wm->lstate.exp_rjs_mag = wm->exp.classic.rjs.mag;
wm->lstate.exp_r_shoulder = wm->exp.classic.r_shoulder;
wm->lstate.exp_l_shoulder = wm->exp.classic.l_shoulder;
wm->lstate.exp_btns = wm->exp.classic.btns;
} else {
wm->lstate.exp_ljs_ang = wm->exp.nunchuk.js.ang;
wm->lstate.exp_ljs_mag = wm->exp.nunchuk.js.mag;
wm->lstate.exp_btns = wm->exp.nunchuk.btns;
wm->lstate.exp_accel = wm->exp.nunchuk.accel;
}
break;
}
case EXP_NONE:
break;
}
}
/**
* @brief Determine if the current state differs significantly from the previous.
* @param wm A pointer to a wiimote_t structure.
* @return 1 if a significant change occurred, 0 if not.
*/
static int state_changed(struct wiimote_t* wm) {
#define STATE_CHANGED(a, b) if (a != b) return 1
#define CROSS_THRESH(last, now, thresh) \
do { \
if (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_ORIENT_THRESH)) { \
if ((diff_f(last.roll, now.roll) >= thresh) || \
(diff_f(last.pitch, now.pitch) >= thresh) || \
(diff_f(last.yaw, now.yaw) >= thresh)) \
{ \
last = now; \
return 1; \
} \
} else { \
if (last.roll != now.roll) return 1; \
if (last.pitch != now.pitch) return 1; \
if (last.yaw != now.yaw) return 1; \
} \
} while (0)
#define CROSS_THRESH_XYZ(last, now, thresh) \
do { \
if (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_ORIENT_THRESH)) { \
if ((diff_f(last.x, now.x) >= thresh) || \
(diff_f(last.y, now.y) >= thresh) || \
(diff_f(last.z, now.z) >= thresh)) \
{ \
last = now; \
return 1; \
} \
} else { \
if (last.x != now.x) return 1; \
if (last.y != now.y) return 1; \
if (last.z != now.z) return 1; \
} \
} while (0)
/* ir */
if (WIIUSE_USING_IR(wm)) {
STATE_CHANGED(wm->lstate.ir_ax, wm->ir.ax);
STATE_CHANGED(wm->lstate.ir_ay, wm->ir.ay);
STATE_CHANGED(wm->lstate.ir_distance, wm->ir.distance);
}
/* accelerometer */
if (WIIUSE_USING_ACC(wm)) {
/* raw accelerometer */
CROSS_THRESH_XYZ(wm->lstate.accel, wm->accel, wm->accel_threshold);
/* orientation */
CROSS_THRESH(wm->lstate.orient, wm->orient, wm->orient_threshold);
}
/* expansion */
switch (wm->exp.type) {
case EXP_NUNCHUK: {
STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.nunchuk.js.ang);
STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.nunchuk.js.mag);
STATE_CHANGED(wm->lstate.exp_btns, wm->exp.nunchuk.btns);
CROSS_THRESH(wm->lstate.exp_orient, wm->exp.nunchuk.orient, wm->exp.nunchuk.orient_threshold);
CROSS_THRESH_XYZ(wm->lstate.exp_accel, wm->exp.nunchuk.accel, wm->exp.nunchuk.accel_threshold);
break;
}
case EXP_CLASSIC: {
STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.classic.ljs.ang);
STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.classic.ljs.mag);
STATE_CHANGED(wm->lstate.exp_rjs_ang, wm->exp.classic.rjs.ang);
STATE_CHANGED(wm->lstate.exp_rjs_mag, wm->exp.classic.rjs.mag);
STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.classic.r_shoulder);
STATE_CHANGED(wm->lstate.exp_l_shoulder, wm->exp.classic.l_shoulder);
STATE_CHANGED(wm->lstate.exp_btns, wm->exp.classic.btns);
break;
}
case EXP_GUITAR_HERO_3: {
STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.gh3.js.ang);
STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.gh3.js.mag);
STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.gh3.whammy_bar);
STATE_CHANGED(wm->lstate.exp_btns, wm->exp.gh3.btns);
break;
}
case EXP_WII_BOARD: {
STATE_CHANGED(wm->lstate.exp_wb_rtr, wm->exp.wb.tr);
STATE_CHANGED(wm->lstate.exp_wb_rtl, wm->exp.wb.tl);
STATE_CHANGED(wm->lstate.exp_wb_rbr, wm->exp.wb.br);
STATE_CHANGED(wm->lstate.exp_wb_rbl, wm->exp.wb.bl);
break;
}
case EXP_MOTION_PLUS:
case EXP_MOTION_PLUS_CLASSIC:
case EXP_MOTION_PLUS_NUNCHUK: {
STATE_CHANGED(wm->lstate.drx, wm->exp.mp.raw_gyro.pitch);
STATE_CHANGED(wm->lstate.dry, wm->exp.mp.raw_gyro.roll);
STATE_CHANGED(wm->lstate.drz, wm->exp.mp.raw_gyro.yaw);
if (wm->exp.type == EXP_MOTION_PLUS_CLASSIC) {
STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.classic.ljs.ang);
STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.classic.ljs.mag);
STATE_CHANGED(wm->lstate.exp_rjs_ang, wm->exp.classic.rjs.ang);
STATE_CHANGED(wm->lstate.exp_rjs_mag, wm->exp.classic.rjs.mag);
STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.classic.r_shoulder);
STATE_CHANGED(wm->lstate.exp_l_shoulder, wm->exp.classic.l_shoulder);
STATE_CHANGED(wm->lstate.exp_btns, wm->exp.classic.btns);
} else {
STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.nunchuk.js.ang);
STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.nunchuk.js.mag);
STATE_CHANGED(wm->lstate.exp_btns, wm->exp.nunchuk.btns);
CROSS_THRESH(wm->lstate.exp_orient, wm->exp.nunchuk.orient, wm->exp.nunchuk.orient_threshold);
CROSS_THRESH_XYZ(wm->lstate.exp_accel, wm->exp.nunchuk.accel, wm->exp.nunchuk.accel_threshold);
}
break;
}
case EXP_NONE: {
break;
}
}
STATE_CHANGED(wm->lstate.btns, wm->btns);
return 0;
}

160
src/main/c/guitar_hero_3.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Guitar Hero 3 expansion device.
*/
#include "guitar_hero_3.h"
#include "dynamics.h" /* for calc_joystick_state */
#include "events.h" /* for handshake_expansion */
#include <stdlib.h> /* for malloc */
#include <string.h> /* for memset */
static void guitar_hero_3_pressed_buttons(struct guitar_hero_3_t* gh3, short now);
/**
* @brief Handle the handshake data from the guitar.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* @return Returns 1 if handshake was successful, 0 if not.
*/
int guitar_hero_3_handshake(struct wiimote_t* wm, struct guitar_hero_3_t* gh3, byte* data, unsigned short len) {
/*
* The good fellows that made the Guitar Hero 3 controller
* failed to factory calibrate the devices. There is no
* calibration data on the device.
*/
gh3->btns = 0;
gh3->btns_held = 0;
gh3->btns_released = 0;
gh3->whammy_bar = 0.0f;
/*
TODO: If we're not using anything from calibration data, why are we
even bothering here?
*/
if (data[0] == 0xFF) {
/*
* Sometimes the data returned here is not correct.
* This might happen because the wiimote is lagging
* behind our initialization sequence.
* To fix this just request the handshake again.
*
* Other times it's just the first 16 bytes are 0xFF,
* but since the next 16 bytes are the same, just use
* those.
*/
if (data[16] == 0xFF) {
/* get the calibration data */
byte* handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
WIIUSE_DEBUG("Guitar Hero 3 handshake appears invalid, trying again.");
wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
return 0;
} else {
data += 16;
}
}
/* joystick stuff */
gh3->js.max.x = GUITAR_HERO_3_JS_MAX_X;
gh3->js.min.x = GUITAR_HERO_3_JS_MIN_X;
gh3->js.center.x = GUITAR_HERO_3_JS_CENTER_X;
gh3->js.max.y = GUITAR_HERO_3_JS_MAX_Y;
gh3->js.min.y = GUITAR_HERO_3_JS_MIN_Y;
gh3->js.center.y = GUITAR_HERO_3_JS_CENTER_Y;
/* handshake done */
wm->exp.type = EXP_GUITAR_HERO_3;
#ifdef WIIUSE_WIN32
wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
#endif
return 1;
}
/**
* @brief The guitar disconnected.
*
* @param cc A pointer to a classic_ctrl_t structure.
*/
void guitar_hero_3_disconnected(struct guitar_hero_3_t* gh3) {
memset(gh3, 0, sizeof(struct guitar_hero_3_t));
}
/**
* @brief Handle guitar event.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param msg The message specified in the event packet.
*/
void guitar_hero_3_event(struct guitar_hero_3_t* gh3, byte* msg) {
guitar_hero_3_pressed_buttons(gh3, from_big_endian_uint16_t(msg + 4));
/* whammy bar */
gh3->whammy_bar = (msg[3] - GUITAR_HERO_3_WHAMMY_BAR_MIN) / (float)(GUITAR_HERO_3_WHAMMY_BAR_MAX - GUITAR_HERO_3_WHAMMY_BAR_MIN);
/* joy stick */
calc_joystick_state(&gh3->js, msg[0], msg[1]);
}
/**
* @brief Find what buttons are pressed.
*
* @param cc A pointer to a classic_ctrl_t structure.
* @param msg The message byte specified in the event packet.
*/
static void guitar_hero_3_pressed_buttons(struct guitar_hero_3_t* gh3, short now) {
/* message is inverted (0 is active, 1 is inactive) */
now = ~now & GUITAR_HERO_3_BUTTON_ALL;
/* pressed now & were pressed, then held */
gh3->btns_held = (now & gh3->btns);
/* were pressed or were held & not pressed now, then released */
gh3->btns_released = ((gh3->btns | gh3->btns_held) & ~now);
/* buttons pressed now */
gh3->btns = now;
}

366
src/main/c/io.c Normal file
View File

@@ -0,0 +1,366 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 (non-OS specific).
*/
#include "io.h"
#include "ir.h" /* for wiiuse_set_ir_mode */
#include "wiiuse_internal.h"
#include "os.h" /* for wiiuse_os_* */
#include <stdlib.h> /* for free, malloc */
/**
* @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 wiiuse_connect()
* @see wiiuse_os_find()
*
* 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 wiiuse_connect() to connect to the found \n
* devices.
*
* This function only delegates to the platform-specific implementation
* wiiuse_os_find.
*
* This function is declared in wiiuse.h
*/
int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
return wiiuse_os_find(wm, max_wiimotes, timeout);
}
/**
* @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 wiiuse_find()
* @see wiiuse_disconnect()
* @see wiiuse_os_connect()
*
* Connect to a number of wiimotes when the address is already set
* in the wiimote_t structures. These addresses are normally set
* by the wiiuse_find() function, but can also be set manually.
*
* This function only delegates to the platform-specific implementation
* wiiuse_os_connect.
*
* This function is declared in wiiuse.h
*/
int wiiuse_connect(struct wiimote_t** wm, int wiimotes) {
return wiiuse_os_connect(wm, wiimotes);
}
/**
* @brief Disconnect a wiimote.
*
* @param wm Pointer to a wiimote_t structure.
*
* @see wiiuse_connect()
* @see wiiuse_os_disconnect()
*
* Note that this will not free the wiimote structure.
*
* This function only delegates to the platform-specific implementation
* wiiuse_os_disconnect.
*
* This function is declared in wiiuse.h
*/
void wiiuse_disconnect(struct wiimote_t* wm) {
wiiuse_os_disconnect(wm);
}
/**
* @brief Wait until specified report arrives and return it
*
* @param wm Pointer to a wiimote_t structure.
* @param buffer Pre-allocated memory to store the received data
* @param bufferLength size of buffer in bytes
*
* Synchronous/blocking, this function will not return until it receives the specified
* report from the Wiimote.
*
*/
void wiiuse_wait_report(struct wiimote_t *wm, int report, byte *buffer, int bufferLength) {
for (;;) {
if (wiiuse_os_read(wm, buffer, bufferLength) > 0) {
if (buffer[0] == report) {
break;
} else {
WIIUSE_DEBUG("(id %i) dropping report 0x%x, waiting for 0x%x", wm->unid, buffer[0], report);
}
}
}
}
/**
* @brief Read memory/register data synchronously
*
* @param wm Pointer to a wiimote_t structure.
* @param memory If set to non-zero, reads EEPROM, otherwise registers
* @param addr Address offset to read from
* @param size How many bytes to read
* @param data Pre-allocated memory to store the received data
*
* Synchronous/blocking read, this function will not return until it receives the specified
* amount of data from the Wiimote.
*
*/
void wiiuse_read_data_sync(struct wiimote_t *wm, byte memory, unsigned addr, unsigned short size, byte *data) {
byte pkt[6];
byte buf[MAX_PAYLOAD];
unsigned n_full_reports;
unsigned last_report;
byte *output;
unsigned int i;
/*
* address in big endian first, the leading byte will
* be overwritten (only 3 bytes are sent)
*/
to_big_endian_uint32_t(pkt, addr);
/* read from registers or memory */
pkt[0] = (memory != 0) ? 0x00 : 0x04;
/* length in big endian */
to_big_endian_uint16_t(pkt + 4, size);
/* send */
wiiuse_send(wm, WM_CMD_READ_DATA, pkt, sizeof(pkt));
/* calculate how many 16B packets we have to get back */
n_full_reports = size / 16;
last_report = size % 16;
output = data;
for (i = 0; i < n_full_reports; ++i) {
wiiuse_wait_report(wm, WM_RPT_READ, buf, MAX_PAYLOAD);
memmove(output, buf + 6, 16);
output += 16;
}
/* read the last incomplete packet */
if (last_report) {
wiiuse_wait_report(wm, WM_RPT_READ, buf, MAX_PAYLOAD);
memmove(output, buf + 6, last_report);
}
}
/**
* @brief Get initialization data from the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param data unused
* @param len unused
*
* When first called for a wiimote_t structure, a request
* is sent to the wiimote for initialization information.
* This includes factory set accelerometer data.
* The handshake will be concluded when the wiimote responds
* with this data.
*/
#ifdef WIIUSE_SYNC_HANDSHAKE
void wiiuse_handshake(struct wiimote_t* wm, byte* data, uint16_t len) {
/* send request to wiimote for accelerometer calibration */
byte buf[MAX_PAYLOAD];
/* step 0 - Reset wiimote */
{
//wiiuse_set_leds(wm, WIIMOTE_LED_NONE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_ACC);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_RUMBLE);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
WIIMOTE_DISABLE_FLAG(wm, WIIUSE_CONTINUOUS);
wiiuse_set_report_type(wm);
wiiuse_millisleep(500);
WIIUSE_DEBUG("Wiimote reset!\n");
}
/* step 1 - calibration of accelerometers */
{
struct accel_t* accel = &wm->accel_calib;
wiiuse_read_data_sync(wm, 1, WM_MEM_OFFSET_CALIBRATION, 8, buf);
/* received read data */
accel->cal_zero.x = buf[0];
accel->cal_zero.y = buf[1];
accel->cal_zero.z = buf[2];
accel->cal_g.x = buf[4] - accel->cal_zero.x;
accel->cal_g.y = buf[5] - accel->cal_zero.y;
accel->cal_g.z = buf[6] - accel->cal_zero.z;
WIIUSE_DEBUG("Calibrated wiimote acc\n");
}
/* step 2 - re-enable IR and ask for status */
{
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
/* now enable IR if it was set before the handshake completed */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
WIIUSE_DEBUG("Handshake finished, enabling IR.");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
wiiuse_set_ir(wm, 1);
}
WIIUSE_DEBUG("Asking for status ...\n");
wm->event = WIIUSE_CONNECT;
wiiuse_status(wm);
}
}
#else
static void wiiuse_disable_motion_plus1(struct wiimote_t *wm, byte *data, unsigned short len);
static void wiiuse_disable_motion_plus2(struct wiimote_t *wm, byte *data, unsigned short len);
void wiiuse_handshake(struct wiimote_t* wm, byte* data, uint16_t len) {
if (!wm) {
return;
}
switch (wm->handshake_state) {
case 0: {
byte* buf;
/* continuous reporting off, report to buttons only */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
wiiuse_set_leds(wm, WIIMOTE_LED_NONE);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_ACC);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_RUMBLE);
WIIMOTE_DISABLE_FLAG(wm, WIIUSE_CONTINUOUS);
wiiuse_set_report_type(wm);
/* send request to wiimote for accelerometer calibration */
buf = (byte*)malloc(sizeof(byte) * 8);
wiiuse_read_data_cb(wm, wiiuse_handshake, buf, WM_MEM_OFFSET_CALIBRATION, 7);
wm->handshake_state++;
wiiuse_set_leds(wm, WIIMOTE_LED_NONE);
break;
}
case 1: {
struct read_req_t* req = wm->read_req;
struct accel_t* accel = &wm->accel_calib;
byte val;
/* received read data */
accel->cal_zero.x = req->buf[0];
accel->cal_zero.y = req->buf[1];
accel->cal_zero.z = req->buf[2];
accel->cal_g.x = req->buf[4] - accel->cal_zero.x;
accel->cal_g.y = req->buf[5] - accel->cal_zero.y;
accel->cal_g.z = req->buf[6] - accel->cal_zero.z;
/* done with the buffer */
free(req->buf);
/* handshake is done */
WIIUSE_DEBUG("Handshake finished. Calibration: Idle: X=%x Y=%x Z=%x\t+1g: X=%x Y=%x Z=%x",
accel->cal_zero.x, accel->cal_zero.y, accel->cal_zero.z,
accel->cal_g.x, accel->cal_g.y, accel->cal_g.z);
/* M+ off */
val = 0x55;
wiiuse_write_data_cb(wm, WM_EXP_MEM_ENABLE1, &val, 1, wiiuse_disable_motion_plus1);
break;
}
case 2: {
/* request the status of the wiimote to check for any expansion */
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
wm->handshake_state++;
/* now enable IR if it was set before the handshake completed */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
WIIUSE_DEBUG("Handshake finished, enabling IR.");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
wiiuse_set_ir(wm, 1);
}
wm->event = WIIUSE_CONNECT;
wiiuse_status(wm);
break;
}
default: {
break;
}
}
}
static void wiiuse_disable_motion_plus1(struct wiimote_t *wm, byte *data, unsigned short len) {
byte val = 0x00;
wiiuse_write_data_cb(wm, WM_EXP_MEM_ENABLE1, &val, 1, wiiuse_disable_motion_plus2);
}
static void wiiuse_disable_motion_plus2(struct wiimote_t *wm, byte *data, unsigned short len) {
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_FAILED);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE);
wiiuse_set_ir_mode(wm);
wm->handshake_state++;
wiiuse_handshake(wm, NULL, 0);
}
#endif

793
src/main/c/ir.c Normal file
View File

@@ -0,0 +1,793 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 IR data.
*/
#include "ir.h"
#include <math.h> /* for atanf, cos, sin, sqrt */
static int get_ir_sens(struct wiimote_t* wm, const byte** block1, const byte** block2);
static void interpret_ir_data(struct wiimote_t* wm);
static void fix_rotated_ir_dots(struct ir_dot_t* dot, float ang);
static void get_ir_dot_avg(struct ir_dot_t* dot, int* x, int* y);
static void reorder_ir_dots(struct ir_dot_t* dot);
static float ir_distance(struct ir_dot_t* dot);
static int ir_correct_for_bounds(int* x, int* y, enum aspect_t aspect, int offset_x, int offset_y);
static void ir_convert_to_vres(int* x, int* y, enum aspect_t aspect, int vx, int vy);
/* ir block data */
static const byte WM_IR_BLOCK1_LEVEL1[] = "\x02\x00\x00\x71\x01\x00\x64\x00\xfe";
static const byte WM_IR_BLOCK2_LEVEL1[] = "\xfd\x05";
static const byte WM_IR_BLOCK1_LEVEL2[] = "\x02\x00\x00\x71\x01\x00\x96\x00\xb4";
static const byte WM_IR_BLOCK2_LEVEL2[] = "\xb3\x04";
static const byte WM_IR_BLOCK1_LEVEL3[] = "\x02\x00\x00\x71\x01\x00\xaa\x00\x64";
static const byte WM_IR_BLOCK2_LEVEL3[] = "\x63\x03";
static const byte WM_IR_BLOCK1_LEVEL4[] = "\x02\x00\x00\x71\x01\x00\xc8\x00\x36";
static const byte WM_IR_BLOCK2_LEVEL4[] = "\x35\x03";
static const byte WM_IR_BLOCK1_LEVEL5[] = "\x07\x00\x00\x71\x01\x00\x72\x00\x20";
static const byte WM_IR_BLOCK2_LEVEL5[] = "\x1f\x03";
void wiiuse_set_ir_mode(struct wiimote_t *wm) {
byte buf = 0x00;
if (!wm) {
return;
}
if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
return;
}
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
buf = WM_IR_TYPE_BASIC;
} else {
buf = WM_IR_TYPE_EXTENDED;
}
wiiuse_write_data(wm, WM_REG_IR_MODENUM, &buf, 1);
}
/**
* @brief Set if the wiimote should track IR targets.
*
* @param wm Pointer to a wiimote_t structure.
* @param status 1 to enable, 0 to disable.
*/
void wiiuse_set_ir(struct wiimote_t* wm, int status) {
byte buf;
const byte* block1 = NULL;
const byte* block2 = NULL;
int ir_level;
if (!wm) {
return;
}
/*
* Wait for the handshake to finish first.
* When it handshake finishes and sees that
* IR is enabled, it will call this function
* again to actually enable IR.
*/
if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE)) {
if (status) {
WIIUSE_DEBUG("Tried to enable IR, will wait until handshake finishes.");
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR);
} /* else ignoring request to turn off, since it's turned off by default */
return;
}
/*
* Check to make sure a sensitivity setting is selected.
*/
ir_level = get_ir_sens(wm, &block1, &block2);
if (!ir_level) {
WIIUSE_ERROR("No IR sensitivity setting selected.");
return;
}
if (status) {
/* if already enabled then stop */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
return;
}
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR);
} else {
/* if already disabled then stop */
if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
return;
}
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
}
/* set camera 1 and 2 */
buf = (status ? 0x04 : 0x00);
wiiuse_send(wm, WM_CMD_IR, &buf, 1);
wiiuse_send(wm, WM_CMD_IR_2, &buf, 1);
if (!status) {
WIIUSE_DEBUG("Disabled IR cameras for wiimote id %i.", wm->unid);
wiiuse_set_report_type(wm);
return;
}
/* enable IR, set sensitivity */
buf = 0x08;
wiiuse_write_data(wm, WM_REG_IR, &buf, 1);
/* wait for the wiimote to catch up */
wiiuse_millisleep(50);
/* write sensitivity blocks */
wiiuse_write_data(wm, WM_REG_IR_BLOCK1, (byte*)block1, 9);
wiiuse_write_data(wm, WM_REG_IR_BLOCK2, (byte*)block2, 2);
/* set the IR mode */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
buf = WM_IR_TYPE_BASIC;
} else {
buf = WM_IR_TYPE_EXTENDED;
}
wiiuse_write_data(wm, WM_REG_IR_MODENUM, &buf, 1);
wiiuse_millisleep(50);
/* set the wiimote report type */
wiiuse_set_report_type(wm);
WIIUSE_DEBUG("Enabled IR camera for wiimote id %i (sensitivity level %i).", wm->unid, ir_level);
}
/**
* @brief Get the IR sensitivity settings.
*
* @param wm Pointer to a wiimote_t structure.
* @param block1 [out] Pointer to where block1 will be set.
* @param block2 [out] Pointer to where block2 will be set.
*
* @return Returns the sensitivity level.
*/
static int get_ir_sens(struct wiimote_t* wm, const byte** block1, const byte** block2) {
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL1)) {
*block1 = WM_IR_BLOCK1_LEVEL1;
*block2 = WM_IR_BLOCK2_LEVEL1;
return 1;
} else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL2)) {
*block1 = WM_IR_BLOCK1_LEVEL2;
*block2 = WM_IR_BLOCK2_LEVEL2;
return 2;
} else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL3)) {
*block1 = WM_IR_BLOCK1_LEVEL3;
*block2 = WM_IR_BLOCK2_LEVEL3;
return 3;
} else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL4)) {
*block1 = WM_IR_BLOCK1_LEVEL4;
*block2 = WM_IR_BLOCK2_LEVEL4;
return 4;
} else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL5)) {
*block1 = WM_IR_BLOCK1_LEVEL5;
*block2 = WM_IR_BLOCK2_LEVEL5;
return 5;
}
*block1 = NULL;
*block2 = NULL;
return 0;
}
/**
* @brief Set the virtual screen resolution for IR tracking.
*
* @param wm Pointer to a wiimote_t structure.
* @param status 1 to enable, 0 to disable.
*/
void wiiuse_set_ir_vres(struct wiimote_t* wm, unsigned int x, unsigned int y) {
if (!wm) {
return;
}
wm->ir.vres[0] = (x - 1);
wm->ir.vres[1] = (y - 1);
}
/**
* @brief Set the XY position for the IR cursor.
*
* @param wm Pointer to a wiimote_t structure.
*/
void wiiuse_set_ir_position(struct wiimote_t* wm, enum ir_position_t pos) {
if (!wm) {
return;
}
wm->ir.pos = pos;
switch (pos) {
case WIIUSE_IR_ABOVE:
wm->ir.offset[0] = 0;
if (wm->ir.aspect == WIIUSE_ASPECT_16_9) {
wm->ir.offset[1] = WM_ASPECT_16_9_Y / 2 - 70;
} else if (wm->ir.aspect == WIIUSE_ASPECT_4_3) {
wm->ir.offset[1] = WM_ASPECT_4_3_Y / 2 - 100;
}
return;
case WIIUSE_IR_BELOW:
wm->ir.offset[0] = 0;
if (wm->ir.aspect == WIIUSE_ASPECT_16_9) {
wm->ir.offset[1] = -WM_ASPECT_16_9_Y / 2 + 100;
} else if (wm->ir.aspect == WIIUSE_ASPECT_4_3) {
wm->ir.offset[1] = -WM_ASPECT_4_3_Y / 2 + 70;
}
return;
default:
return;
};
}
/**
* @brief Set the aspect ratio of the TV/monitor.
*
* @param wm Pointer to a wiimote_t structure.
* @param aspect Either WIIUSE_ASPECT_16_9 or WIIUSE_ASPECT_4_3
*/
void wiiuse_set_aspect_ratio(struct wiimote_t* wm, enum aspect_t aspect) {
if (!wm) {
return;
}
wm->ir.aspect = aspect;
if (aspect == WIIUSE_ASPECT_4_3) {
wm->ir.vres[0] = WM_ASPECT_4_3_X;
wm->ir.vres[1] = WM_ASPECT_4_3_Y;
} else {
wm->ir.vres[0] = WM_ASPECT_16_9_X;
wm->ir.vres[1] = WM_ASPECT_16_9_Y;
}
/* reset the position offsets */
wiiuse_set_ir_position(wm, wm->ir.pos);
}
/**
* @brief Set the IR sensitivity.
*
* @param wm Pointer to a wiimote_t structure.
* @param level 1-5, same as Wii system sensitivity setting.
*
* If the level is < 1, then level will be set to 1.
* If the level is > 5, then level will be set to 5.
*/
void wiiuse_set_ir_sensitivity(struct wiimote_t* wm, int level) {
const byte* block1 = NULL;
const byte* block2 = NULL;
if (!wm) {
return;
}
if (level > 5) {
level = 5;
}
if (level < 1) {
level = 1;
}
WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_IR_SENS_LVL1 |
WIIMOTE_STATE_IR_SENS_LVL2 |
WIIMOTE_STATE_IR_SENS_LVL3 |
WIIMOTE_STATE_IR_SENS_LVL4 |
WIIMOTE_STATE_IR_SENS_LVL5));
switch (level) {
case 1:
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL1);
break;
case 2:
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL2);
break;
case 3:
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL3);
break;
case 4:
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL4);
break;
case 5:
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL5);
break;
default:
return;
}
/* set the new sensitivity */
get_ir_sens(wm, &block1, &block2);
wiiuse_write_data(wm, WM_REG_IR_BLOCK1, block1, 9);
wiiuse_write_data(wm, WM_REG_IR_BLOCK2, block2, 2);
WIIUSE_DEBUG("Set IR sensitivity to level %i (unid %i)", level, wm->unid);
}
/**
* @brief Calculate the data from the IR spots. Basic IR mode.
*
* @param wm Pointer to a wiimote_t structure.
* @param data Data returned by the wiimote for the IR spots.
*/
void calculate_basic_ir(struct wiimote_t* wm, byte* data) {
struct ir_dot_t* dot = wm->ir.dot;
int i;
dot[0].rx = 1023 - (data[0] | ((data[2] & 0x30) << 4));
dot[0].ry = data[1] | ((data[2] & 0xC0) << 2);
dot[1].rx = 1023 - (data[3] | ((data[2] & 0x03) << 8));
dot[1].ry = data[4] | ((data[2] & 0x0C) << 6);
dot[2].rx = 1023 - (data[5] | ((data[7] & 0x30) << 4));
dot[2].ry = data[6] | ((data[7] & 0xC0) << 2);
dot[3].rx = 1023 - (data[8] | ((data[7] & 0x03) << 8));
dot[3].ry = data[9] | ((data[7] & 0x0C) << 6);
/* set each IR spot to visible if spot is in range */
for (i = 0; i < 4; ++i) {
if (dot[i].ry == 1023) {
dot[i].visible = 0;
} else {
dot[i].visible = 1;
dot[i].size = 0; /* since we don't know the size, set it as 0 */
}
}
interpret_ir_data(wm);
}
/**
* @brief Calculate the data from the IR spots. Extended IR mode.
*
* @param wm Pointer to a wiimote_t structure.
* @param data Data returned by the wiimote for the IR spots.
*/
void calculate_extended_ir(struct wiimote_t* wm, byte* data) {
struct ir_dot_t* dot = wm->ir.dot;
int i;
for (i = 0; i < 4; ++i) {
dot[i].rx = 1023 - (data[3 * i] | ((data[(3 * i) + 2] & 0x30) << 4));
dot[i].ry = data[(3 * i) + 1] | ((data[(3 * i) + 2] & 0xC0) << 2);
dot[i].size = data[(3 * i) + 2] & 0x0F;
/* if in range set to visible */
if (dot[i].ry == 1023) {
dot[i].visible = 0;
} else {
dot[i].visible = 1;
}
}
interpret_ir_data(wm);
}
/**
* @brief Interpret IR data into more user friendly variables.
*
* @param wm Pointer to a wiimote_t structure.
*/
static void interpret_ir_data(struct wiimote_t* wm) {
struct ir_dot_t* dot = wm->ir.dot;
int i;
float roll = 0.0f;
int last_num_dots = wm->ir.num_dots;
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC)) {
roll = wm->orient.roll;
}
/* count visible dots */
wm->ir.num_dots = 0;
for (i = 0; i < 4; ++i) {
if (dot[i].visible) {
wm->ir.num_dots++;
}
}
switch (wm->ir.num_dots) {
case 0: {
wm->ir.state = 0;
/* reset the dot ordering */
for (i = 0; i < 4; ++i) {
dot[i].order = 0;
}
wm->ir.x = 0;
wm->ir.y = 0;
wm->ir.z = 0.0f;
return;
}
case 1: {
fix_rotated_ir_dots(wm->ir.dot, roll);
if (wm->ir.state < 2) {
/*
* Only 1 known dot, so use just that.
*/
for (i = 0; i < 4; ++i) {
if (dot[i].visible) {
wm->ir.x = dot[i].x;
wm->ir.y = dot[i].y;
wm->ir.ax = wm->ir.x;
wm->ir.ay = wm->ir.y;
/* can't calculate yaw because we don't have the distance */
/* wm->orient.yaw = calc_yaw(&wm->ir); */
ir_convert_to_vres(&wm->ir.x, &wm->ir.y, wm->ir.aspect, wm->ir.vres[0], wm->ir.vres[1]);
break;
}
}
} else {
/*
* Only see 1 dot but know theres 2.
* Try to estimate where the other one
* should be and use that.
*/
for (i = 0; i < 4; ++i) {
if (dot[i].visible) {
int ox = 0;
int x, y;
if (dot[i].order == 1)
/* visible is the left dot - estimate where the right is */
{
ox = (int32_t)(dot[i].x + wm->ir.distance);
} else if (dot[i].order == 2)
/* visible is the right dot - estimate where the left is */
{
ox = (int32_t)(dot[i].x - wm->ir.distance);
}
x = ((signed int)dot[i].x + ox) / 2;
y = dot[i].y;
wm->ir.ax = x;
wm->ir.ay = y;
wm->orient.yaw = calc_yaw(&wm->ir);
if (ir_correct_for_bounds(&x, &y, wm->ir.aspect, wm->ir.offset[0], wm->ir.offset[1])) {
ir_convert_to_vres(&x, &y, wm->ir.aspect, wm->ir.vres[0], wm->ir.vres[1]);
wm->ir.x = x;
wm->ir.y = y;
}
break;
}
}
}
break;
}
case 2:
case 3:
case 4: {
/*
* Two (or more) dots known and seen.
* Average them together to estimate the true location.
*/
int x, y;
wm->ir.state = 2;
fix_rotated_ir_dots(wm->ir.dot, roll);
/* if there is at least 1 new dot, reorder them all */
if (wm->ir.num_dots > last_num_dots) {
reorder_ir_dots(dot);
wm->ir.x = 0;
wm->ir.y = 0;
}
wm->ir.distance = ir_distance(dot);
wm->ir.z = 1023 - wm->ir.distance;
get_ir_dot_avg(wm->ir.dot, &x, &y);
wm->ir.ax = x;
wm->ir.ay = y;
wm->orient.yaw = calc_yaw(&wm->ir);
if (ir_correct_for_bounds(&x, &y, wm->ir.aspect, wm->ir.offset[0], wm->ir.offset[1])) {
ir_convert_to_vres(&x, &y, wm->ir.aspect, wm->ir.vres[0], wm->ir.vres[1]);
wm->ir.x = x;
wm->ir.y = y;
}
break;
}
default: {
break;
}
}
#ifdef WITH_WIIUSE_DEBUG
{
int ir_level;
WIIUSE_GET_IR_SENSITIVITY(wm, &ir_level);
WIIUSE_DEBUG("IR sensitivity: %i", ir_level);
WIIUSE_DEBUG("IR visible dots: %i", wm->ir.num_dots);
for (i = 0; i < 4; ++i)
if (dot[i].visible) {
WIIUSE_DEBUG("IR[%i][order %i] (%.3i, %.3i) -> (%.3i, %.3i)", i, dot[i].order, dot[i].rx, dot[i].ry, dot[i].x, dot[i].y);
}
WIIUSE_DEBUG("IR[absolute]: (%i, %i)", wm->ir.x, wm->ir.y);
}
#endif
}
/**
* @brief Fix the rotation of the IR dots.
*
* @param dot An array of 4 ir_dot_t objects.
* @param ang The roll angle to correct by (-180, 180)
*
* If there is roll then the dots are rotated
* around the origin and give a false cursor
* position. Correct for the roll.
*
* If the accelerometer is off then obviously
* this will not do anything and the cursor
* position may be inaccurate.
*/
static void fix_rotated_ir_dots(struct ir_dot_t* dot, float ang) {
float s, c;
int x, y;
int i;
if (!ang) {
for (i = 0; i < 4; ++i) {
dot[i].x = dot[i].rx;
dot[i].y = dot[i].ry;
}
return;
}
s = sinf(DEGREE_TO_RAD(ang));
c = cosf(DEGREE_TO_RAD(ang));
/*
* [ cos(theta) -sin(theta) ][ ir->rx ]
* [ sin(theta) cos(theta) ][ ir->ry ]
*/
for (i = 0; i < 4; ++i) {
if (!dot[i].visible) {
continue;
}
x = dot[i].rx - (1024 / 2);
y = dot[i].ry - (768 / 2);
dot[i].x = (uint32_t)((c * x) + (-s * y));
dot[i].y = (uint32_t)((s * x) + (c * y));
dot[i].x += (1024 / 2);
dot[i].y += (768 / 2);
}
}
/**
* @brief Average IR dots.
*
* @param dot An array of 4 ir_dot_t objects.
* @param x [out] Average X
* @param y [out] Average Y
*/
static void get_ir_dot_avg(struct ir_dot_t* dot, int* x, int* y) {
int vis = 0, i = 0;
*x = 0;
*y = 0;
for (; i < 4; ++i) {
if (dot[i].visible) {
*x += dot[i].x;
*y += dot[i].y;
++vis;
}
}
*x /= vis;
*y /= vis;
}
/**
* @brief Reorder the IR dots.
*
* @param dot An array of 4 ir_dot_t objects.
*/
static void reorder_ir_dots(struct ir_dot_t* dot) {
int i, j, order;
/* reset the dot ordering */
for (i = 0; i < 4; ++i) {
dot[i].order = 0;
}
for (order = 1; order < 5; ++order) {
i = 0;
for (; !dot[i].visible || dot[i].order; ++i)
if (i >= 3) {
return;
}
for (j = 0; j < 4; ++j) {
if (dot[j].visible && !dot[j].order && (dot[j].x < dot[i].x)) {
i = j;
}
}
dot[i].order = order;
}
}
/**
* @brief Calculate the distance between the first 2 visible IR dots.
*
* @param dot An array of 4 ir_dot_t objects.
*/
static float ir_distance(struct ir_dot_t* dot) {
int i1, i2;
int xd, yd;
for (i1 = 0; i1 < 4; ++i1)
if (dot[i1].visible) {
break;
}
if (i1 == 4) {
return 0.0f;
}
for (i2 = i1 + 1; i2 < 4; ++i2)
if (dot[i2].visible) {
break;
}
if (i2 == 4) {
return 0.0f;
}
xd = dot[i2].x - dot[i1].x;
yd = dot[i2].y - dot[i1].y;
return sqrtf(xd * xd + yd * yd);
}
/**
* @brief Correct for the IR bounding box.
*
* @param x [out] The current X, it will be updated if valid.
* @param y [out] The current Y, it will be updated if valid.
* @param aspect Aspect ratio of the screen.
* @param offset_x The X offset of the bounding box.
* @param offset_y The Y offset of the bounding box.
*
* @return Returns 1 if the point is valid and was updated.
*
* Nintendo was smart with this bit. They sacrifice a little
* precision for a big increase in usability.
*/
static int ir_correct_for_bounds(int* x, int* y, enum aspect_t aspect, int offset_x, int offset_y) {
int x0, y0;
int xs, ys;
if (aspect == WIIUSE_ASPECT_16_9) {
xs = WM_ASPECT_16_9_X;
ys = WM_ASPECT_16_9_Y;
} else {
xs = WM_ASPECT_4_3_X;
ys = WM_ASPECT_4_3_Y;
}
x0 = ((1024 - xs) / 2) + offset_x;
y0 = ((768 - ys) / 2) + offset_y;
if ((*x >= x0)
&& (*x <= (x0 + xs))
&& (*y >= y0)
&& (*y <= (y0 + ys))) {
*x -= offset_x;
*y -= offset_y;
return 1;
}
return 0;
}
/**
* @brief Interpolate the point to the user defined virtual screen resolution.
*/
static void ir_convert_to_vres(int* x, int* y, enum aspect_t aspect, int vx, int vy) {
int xs, ys;
if (aspect == WIIUSE_ASPECT_16_9) {
xs = WM_ASPECT_16_9_X;
ys = WM_ASPECT_16_9_Y;
} else {
xs = WM_ASPECT_4_3_X;
ys = WM_ASPECT_4_3_Y;
}
*x -= ((1024 - xs) / 2);
*y -= ((768 - ys) / 2);
*x = (int)((*x / (float)xs) * vx);
*y = (int)((*y / (float)ys) * vy);
}
/**
* @brief Calculate yaw given the IR data.
*
* @param ir IR data structure.
*/
float calc_yaw(struct ir_t* ir) {
float x;
x = (float)(ir->ax - 512);
x = x * (ir->z / 1024.0f);
return RAD_TO_DEGREE(atanf(x / ir->z));
}

339
src/main/c/motion_plus.c Normal file
View File

@@ -0,0 +1,339 @@
/*
* wiiuse
*
* Written By:
* Michal Wiedenbauer < shagkur >
* Dave Murphy < WinterMute >
* Hector Martin < marcan >
* Radu Andries <admiral0>
*
* Copyright 2009
*
* This file is part of wiiuse and fWIIne.
*
* 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$
*
*/
#include "motion_plus.h"
#include "io.h" /* for wiiuse_read */
#include "events.h" /* for disable_expansion */
#include "ir.h" /* for wiiuse_set_ir_mode */
#include "nunchuk.h" /* for nunchuk_pressed_buttons */
#include "dynamics.h" /* for calc_joystick_state, etc */
#include <string.h> /* for memset */
#include <math.h> /* for fabs */
static void wiiuse_calibrate_motion_plus(struct motion_plus_t *mp);
static void calculate_gyro_rates(struct motion_plus_t* mp);
void wiiuse_probe_motion_plus(struct wiimote_t *wm) {
byte buf[MAX_PAYLOAD];
unsigned id;
wiiuse_read_data_sync(wm, 0, WM_EXP_MOTION_PLUS_IDENT, 6, buf);
/* check error code */
if (buf[4] & 0x0f) {
WIIUSE_DEBUG("No Motion+ available, stopping probe.");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_MPLUS_PRESENT);
return;
}
/* decode the id */
id = from_big_endian_uint32_t(buf + 2);
if (id != EXP_ID_CODE_INACTIVE_MOTION_PLUS &&
id != EXP_ID_CODE_INACTIVE_MOTION_PLUS_BUILTIN &&
id != EXP_ID_CODE_NLA_MOTION_PLUS &&
id != EXP_ID_CODE_NLA_MOTION_PLUS_NUNCHUK &&
id != EXP_ID_CODE_NLA_MOTION_PLUS_CLASSIC) {
/* we have read something weird */
WIIUSE_DEBUG("Motion+ ID doesn't match, probably not connected.");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_MPLUS_PRESENT);
return;
}
WIIUSE_DEBUG("Detected inactive Motion+!");
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_MPLUS_PRESENT);
/* init M+ */
buf[0] = 0x55;
wiiuse_write_data(wm, WM_EXP_MOTION_PLUS_INIT, buf, 1);
/* Init whatever is hanging on the pass-through port */
buf[0] = 0x55;
wiiuse_write_data(wm, WM_EXP_MEM_ENABLE1, buf, 1);
buf[0] = 0x00;
wiiuse_write_data(wm, WM_EXP_MEM_ENABLE2, buf, 1);
/* Init gyroscope data */
wm->exp.mp.cal_gyro.roll = 0;
wm->exp.mp.cal_gyro.pitch = 0;
wm->exp.mp.cal_gyro.yaw = 0;
wm->exp.mp.orient.roll = 0.0;
wm->exp.mp.orient.pitch = 0.0;
wm->exp.mp.orient.yaw = 0.0;
wm->exp.mp.raw_gyro_threshold = 10;
wm->exp.mp.nc = &(wm->exp.nunchuk);
wm->exp.mp.classic = &(wm->exp.classic);
wm->exp.nunchuk.flags = &wm->flags;
wm->exp.mp.ext = 0;
wiiuse_set_ir_mode(wm);
wiiuse_set_report_type(wm);
}
void wiiuse_motion_plus_handshake(struct wiimote_t *wm, byte *data, unsigned short len) {
uint32_t val;
if (data == NULL) {
wiiuse_read_data_cb(wm, wiiuse_motion_plus_handshake, wm->motion_plus_id, WM_EXP_ID, 6);
} else {
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_FAILED);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP); /* tell wiimote to include exp. data in reports */
val = from_big_endian_uint32_t(data + 2);
if (val == EXP_ID_CODE_MOTION_PLUS ||
val == EXP_ID_CODE_MOTION_PLUS_NUNCHUK ||
val == EXP_ID_CODE_MOTION_PLUS_CLASSIC) {
/* handshake done */
wm->event = WIIUSE_MOTION_PLUS_ACTIVATED;
switch (val) {
case EXP_ID_CODE_MOTION_PLUS:
wm->exp.type = EXP_MOTION_PLUS;
break;
case EXP_ID_CODE_MOTION_PLUS_NUNCHUK:
wm->exp.type = EXP_MOTION_PLUS_NUNCHUK;
break;
case EXP_ID_CODE_MOTION_PLUS_CLASSIC:
wm->exp.type = EXP_MOTION_PLUS_CLASSIC;
break;
default:
/* huh? */
WIIUSE_WARNING("Unknown ID returned in Motion+ handshake %d\n", val);
wm->exp.type = EXP_MOTION_PLUS;
break;
}
WIIUSE_DEBUG("Motion plus connected");
/* Init gyroscopes */
wm->exp.mp.cal_gyro.roll = 0;
wm->exp.mp.cal_gyro.pitch = 0;
wm->exp.mp.cal_gyro.yaw = 0;
wm->exp.mp.orient.roll = 0.0;
wm->exp.mp.orient.pitch = 0.0;
wm->exp.mp.orient.yaw = 0.0;
wm->exp.mp.raw_gyro_threshold = 10;
wm->exp.mp.nc = &(wm->exp.nunchuk);
wm->exp.mp.classic = &(wm->exp.classic);
wm->exp.nunchuk.flags = &wm->flags;
wm->exp.mp.ext = 0;
wiiuse_set_ir_mode(wm);
wiiuse_set_report_type(wm);
}
}
}
static void wiiuse_set_motion_plus_clear2(struct wiimote_t *wm, byte *data, unsigned short len) {
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_FAILED);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE);
wiiuse_set_ir_mode(wm);
wiiuse_status(wm);
}
static void wiiuse_set_motion_plus_clear1(struct wiimote_t *wm, byte *data, unsigned short len) {
byte val = 0x00;
wiiuse_write_data_cb(wm, WM_EXP_MEM_ENABLE1, &val, 1, wiiuse_set_motion_plus_clear2);
}
/**
* @brief Enable/disable Motion+ expansion
*
* @param wm Pointer to the wiimote with Motion+
* @param status 0 - off, 1 - on, standalone, 2 - nunchuk pass-through
*
*/
void wiiuse_set_motion_plus(struct wiimote_t *wm, int status) {
byte val;
if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_MPLUS_PRESENT) ||
WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP_HANDSHAKE)) {
return;
}
if (status) {
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE);
val = (status == 1) ? 0x04 : 0x05;
wiiuse_write_data_cb(wm, WM_EXP_MOTION_PLUS_ENABLE, &val, 1, wiiuse_motion_plus_handshake);
} else {
disable_expansion(wm);
val = 0x55;
wiiuse_write_data_cb(wm, WM_EXP_MEM_ENABLE1, &val, 1, wiiuse_set_motion_plus_clear1);
}
}
void motion_plus_disconnected(struct motion_plus_t* mp) {
WIIUSE_DEBUG("Motion plus disconnected");
memset(mp, 0, sizeof(struct motion_plus_t));
}
void motion_plus_event(struct motion_plus_t* mp, int exp_type, byte* msg) {
/*
* Pass-through modes interleave data from the gyro
* with the expansion data. This extracts the tag
* determining which is which
*/
int isMPFrame = (1 << 1) & msg[5];
mp->ext = msg[4] & 0x1; /* extension attached to pass-through port? */
if (mp->ext == 0 || isMPFrame) { /* reading gyro frame */
/* Check if the gyroscope is in fast or slow mode (0 if rotating fast, 1 if slow or still) */
mp->acc_mode = ((msg[4] & 0x2) << 1) | ((msg[3] & 0x1) << 1) | ((msg[3] & 0x2) >> 1);
mp->raw_gyro.roll = ((msg[4] & 0xFC) << 6) | msg[1];
mp->raw_gyro.pitch = ((msg[5] & 0xFC) << 6) | msg[2];
mp->raw_gyro.yaw = ((msg[3] & 0xFC) << 6) | msg[0];
/* First calibration */
if ((mp->raw_gyro.roll > 5000) &&
(mp->raw_gyro.pitch > 5000) &&
(mp->raw_gyro.yaw > 5000) &&
(mp->raw_gyro.roll < 0x3fff) &&
(mp->raw_gyro.pitch < 0x3fff) &&
(mp->raw_gyro.yaw < 0x3fff) &&
!(mp->cal_gyro.roll) &&
!(mp->cal_gyro.pitch) &&
!(mp->cal_gyro.yaw)) {
wiiuse_calibrate_motion_plus(mp);
}
/* Calculate angular rates in deg/sec and performs some simple filtering */
calculate_gyro_rates(mp);
}
else {
/* expansion frame */
if (exp_type == EXP_MOTION_PLUS_NUNCHUK) {
/* ok, this is nunchuck, re-encode it as regular nunchuck packet */
/* get button states */
nunchuk_pressed_buttons(mp->nc, (msg[5] >> 2));
/* calculate joystick state */
calc_joystick_state(&(mp->nc->js), msg[0], msg[1]);
/* calculate orientation */
mp->nc->accel.x = msg[2];
mp->nc->accel.y = msg[3];
mp->nc->accel.z = (msg[4] & 0xFE) | ((msg[5] >> 5) & 0x04);
calculate_orientation(&(mp->nc->accel_calib),
&(mp->nc->accel),
&(mp->nc->orient),
NUNCHUK_IS_FLAG_SET(mp->nc, WIIUSE_SMOOTHING));
calculate_gforce(&(mp->nc->accel_calib),
&(mp->nc->accel),
&(mp->nc->gforce));
}
else if (exp_type == EXP_MOTION_PLUS_CLASSIC) {
WIIUSE_ERROR("Classic controller pass-through is not implemented!\n");
}
else {
WIIUSE_ERROR("Unsupported mode passed to motion_plus_event() !\n");
}
}
}
/**
* @brief Calibrate the Motion Plus gyroscopes.
*
* @param mp Pointer to a motion_plus_t structure.
*
* This should be called only after receiving the first values
* from the Motion Plus.
*/
void wiiuse_calibrate_motion_plus(struct motion_plus_t *mp) {
mp->cal_gyro.roll = mp->raw_gyro.roll;
mp->cal_gyro.pitch = mp->raw_gyro.pitch;
mp->cal_gyro.yaw = mp->raw_gyro.yaw;
mp->orient.roll = 0.0;
mp->orient.pitch = 0.0;
mp->orient.yaw = 0.0;
}
static void calculate_gyro_rates(struct motion_plus_t* mp) {
short int tmp_r, tmp_p, tmp_y;
float tmp_roll, tmp_pitch, tmp_yaw;
/* We consider calibration data */
tmp_r = mp->raw_gyro.roll - mp->cal_gyro.roll;
tmp_p = mp->raw_gyro.pitch - mp->cal_gyro.pitch;
tmp_y = mp->raw_gyro.yaw - mp->cal_gyro.yaw;
/* We convert to degree/sec according to fast/slow mode */
if (mp->acc_mode & 0x04) {
tmp_roll = (float)tmp_r / 20.0f;
} else {
tmp_roll = (float)tmp_r / 4.0f;
}
if (mp->acc_mode & 0x02) {
tmp_pitch = (float)tmp_p / 20.0f;
} else {
tmp_pitch = (float)tmp_p / 4.0f;
}
if (mp->acc_mode & 0x01) {
tmp_yaw = (float)tmp_y / 20.0f;
} else {
tmp_yaw = (float)tmp_y / 4.0f;
}
/* Simple filtering */
if (fabs(tmp_roll) < 0.5f) {
tmp_roll = 0.0f;
}
if (fabs(tmp_pitch) < 0.5f) {
tmp_pitch = 0.0f;
}
if (fabs(tmp_yaw) < 0.5f) {
tmp_yaw = 0.0f;
}
mp->angle_rate_gyro.roll = tmp_roll;
mp->angle_rate_gyro.pitch = tmp_pitch;
mp->angle_rate_gyro.yaw = tmp_yaw;
}

212
src/main/c/nunchuk.c Normal file
View File

@@ -0,0 +1,212 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Nunchuk expansion device.
*/
#include "nunchuk.h"
#include "dynamics.h" /* for calc_joystick_state, etc */
#include "events.h" /* for handshake_expansion */
#include <stdlib.h> /* for malloc */
#include <string.h> /* for memset */
/**
* @brief Handle the handshake data from the nunchuk.
*
* @param nc A pointer to a nunchuk_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* @return Returns 1 if handshake was successful, 0 if not.
*/
#define HANDSHAKE_BYTES_USED 14
int nunchuk_handshake(struct wiimote_t* wm, struct nunchuk_t* nc, byte* data, unsigned short len) {
nc->btns = 0;
nc->btns_held = 0;
nc->btns_released = 0;
/* set the smoothing to the same as the wiimote */
nc->flags = &wm->flags;
nc->accel_calib.st_alpha = wm->accel_calib.st_alpha;
if (data[0] == 0xFF || len < HANDSHAKE_BYTES_USED) {
/*
* Sometimes the data returned here is not correct.
* This might happen because the wiimote is lagging
* behind our initialization sequence.
* To fix this just request the handshake again.
*
* Other times it's just the first 16 bytes are 0xFF,
* but since the next 16 bytes are the same, just use
* those.
*/
if (len < 17 || len < HANDSHAKE_BYTES_USED + 16 || data[16] == 0xFF) {
/* get the calibration data */
byte* handshake_buf = (byte *)malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
WIIUSE_DEBUG("Nunchuk handshake appears invalid, trying again.");
wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
return 0;
} else {
data += 16;
}
}
nc->accel_calib.cal_zero.x = data[0];
nc->accel_calib.cal_zero.y = data[1];
nc->accel_calib.cal_zero.z = data[2];
nc->accel_calib.cal_g.x = data[4];
nc->accel_calib.cal_g.y = data[5];
nc->accel_calib.cal_g.z = data[6];
nc->js.max.x = data[8];
nc->js.min.x = data[9];
nc->js.center.x = data[10];
nc->js.max.y = data[11];
nc->js.min.y = data[12];
nc->js.center.y = data[13];
WIIUSE_DEBUG("Nunchuk calibration X: min %x, max %x, center %x Y: min %x, max %x, center %x",
nc->js.min.x, nc->js.max.x, nc->js.center.x,
nc->js.min.y, nc->js.max.y, nc->js.center.y);
/* If the calibration data makes no sense, fake it. */
if (nc->js.max.x < nc->js.center.x) {
nc->js.max.x = 255;
}
if (nc->js.max.y < nc->js.center.y) {
nc->js.max.y = 255;
}
/* default the thresholds to the same as the wiimote */
nc->orient_threshold = wm->orient_threshold;
nc->accel_threshold = wm->accel_threshold;
/* handshake done */
wm->exp.type = EXP_NUNCHUK;
#ifdef WIIUSE_WIN32
wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
#endif
return 1;
}
/**
* @brief The nunchuk disconnected.
*
* @param nc A pointer to a nunchuk_t structure.
*/
void nunchuk_disconnected(struct nunchuk_t* nc) {
memset(nc, 0, sizeof(struct nunchuk_t));
}
/**
* @brief Handle nunchuk event.
*
* @param nc A pointer to a nunchuk_t structure.
* @param msg The message specified in the event packet.
*/
void nunchuk_event(struct nunchuk_t* nc, byte* msg) {
/* get button states */
nunchuk_pressed_buttons(nc, msg[5]);
/* calculate joystick state */
calc_joystick_state(&nc->js, msg[0], msg[1]);
/* calculate orientation */
nc->accel.x = msg[2];
nc->accel.y = msg[3];
nc->accel.z = msg[4];
calculate_orientation(&nc->accel_calib, &nc->accel, &nc->orient, NUNCHUK_IS_FLAG_SET(nc, WIIUSE_SMOOTHING));
calculate_gforce(&nc->accel_calib, &nc->accel, &nc->gforce);
}
/**
* @brief Find what buttons are pressed.
*
* @param nc Pointer to a nunchuk_t structure.
* @param msg The message byte specified in the event packet.
*/
void nunchuk_pressed_buttons(struct nunchuk_t* nc, byte now) {
/* message is inverted (0 is active, 1 is inactive) */
now = ~now & NUNCHUK_BUTTON_ALL;
/* pressed now & were pressed, then held */
nc->btns_held = (now & nc->btns);
/* were pressed or were held & not pressed now, then released */
nc->btns_released = ((nc->btns | nc->btns_held) & ~now);
/* buttons pressed now */
nc->btns = now;
}
/**
* @brief Set the orientation event threshold for the nunchuk.
*
* @param wm Pointer to a wiimote_t structure with a nunchuk attached.
* @param threshold The decimal place that should be considered a significant change.
*
* See wiiuse_set_orient_threshold() for details.
*/
void wiiuse_set_nunchuk_orient_threshold(struct wiimote_t* wm, float threshold) {
if (!wm) {
return;
}
wm->exp.nunchuk.orient_threshold = threshold;
}
/**
* @brief Set the accelerometer event threshold for the nunchuk.
*
* @param wm Pointer to a wiimote_t structure with a nunchuk attached.
* @param threshold The decimal place that should be considered a significant change.
*
* See wiiuse_set_orient_threshold() for details.
*/
void wiiuse_set_nunchuk_accel_threshold(struct wiimote_t* wm, int threshold) {
if (!wm) {
return;
}
wm->exp.nunchuk.accel_threshold = threshold;
}

View File

@@ -0,0 +1,92 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 OS X.
*/
#ifdef __APPLE__
#define BLUETOOTH_VERSION_USE_CURRENT
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
#import "../wiiuse_internal.h"
#if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
#define WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE 1
#else
#define WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE 0
#endif
@interface WiiuseWiimote : NSObject<IOBluetoothL2CAPChannelDelegate> {
wiimote* wm; // reference to the C wiimote struct
IOBluetoothDevice* device;
IOBluetoothL2CAPChannel* controlChannel;
IOBluetoothL2CAPChannel* interruptChannel;
IOBluetoothUserNotification* disconnectNotification;
NSMutableArray* receivedData; // a queue of NSObject<WiiuseReceivedMessage>*
NSLock* receivedDataLock;
}
- (id) initWithPtr: (wiimote*) wm device: (IOBluetoothDevice*) device;
- (IOReturn) connect;
- (void) disconnect;
- (int) readBuffer: (byte*) buffer length: (NSUInteger) bufferLength;
- (int) writeReport: (byte) report_type buffer: (byte*) buffer length: (NSUInteger) length;
@end
@protocol WiiuseReceivedMessage <NSObject>
- (int) applyToStruct: (wiimote*) wm buffer: (byte*) buffer length: (NSUInteger) bufferLength; // <0: not copied, 0: copied empty, >0: copied
@end
@interface WiiuseReceivedData : NSObject<WiiuseReceivedMessage> {
NSData* data;
}
- (id) initWithBytes: (void*) bytes length: (NSUInteger) length;
- (id) initWithData: (NSData*) data;
@end
@interface WiiuseDisconnectionMessage : NSObject<WiiuseReceivedMessage> {
}
@end
#endif // __APPLE__

351
src/main/c/os_mac/os_mac.m Normal file
View File

@@ -0,0 +1,351 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 OS X.
*/
#ifdef __APPLE__
#import "os_mac.h"
#import "../events.h"
#import <IOBluetooth/IOBluetoothUtilities.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#import <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
@implementation WiiuseWiimote
#pragma mark init, dealloc
- (id) initWithPtr: (wiimote*) wm_ device:(IOBluetoothDevice *)device_ {
self = [super init];
if(self) {
wm = wm_;
device = [device_ retain];
controlChannel = nil;
interruptChannel = nil;
disconnectNotification = nil;
receivedData = [[NSMutableArray alloc] initWithCapacity: 2];
receivedDataLock = [[NSLock alloc] init];
}
return self;
}
- (void) dealloc {
wm = NULL;
[interruptChannel release];
[controlChannel release];
[device release];
[disconnectNotification unregister];
[disconnectNotification release];
[receivedData release];
[super dealloc];
}
#pragma mark connect, disconnect
- (BOOL) connectChannel: (IOBluetoothL2CAPChannel**) pChannel PSM: (BluetoothL2CAPPSM) psm {
if ([device openL2CAPChannelSync:pChannel withPSM:psm delegate:self] != kIOReturnSuccess) {
WIIUSE_ERROR("Unable to open L2CAP channel [id %i].", wm->unid);
*pChannel = nil;
return NO;
} else {
[*pChannel retain];
return YES;
}
}
- (IOReturn) connect {
if(!device) {
WIIUSE_ERROR("Missing device.");
return kIOReturnBadArgument;
}
// open channels
if(![self connectChannel:&controlChannel PSM:kBluetoothL2CAPPSMHIDControl]) {
[self disconnect];
return kIOReturnNotOpen;
} else if(![self connectChannel:&interruptChannel PSM:kBluetoothL2CAPPSMHIDInterrupt]) {
[self disconnect];
return kIOReturnNotOpen;
}
// register for device disconnection
disconnectNotification = [device registerForDisconnectNotification:self selector:@selector(disconnected:fromDevice:)];
if(!disconnectNotification) {
WIIUSE_ERROR("Unable to register disconnection handler [id %i].", wm->unid);
[self disconnect];
return kIOReturnNotOpen;
}
return kIOReturnSuccess;
}
- (void) disconnectChannel: (IOBluetoothL2CAPChannel**) pChannel {
if(!pChannel) return;
if([*pChannel closeChannel] != kIOReturnSuccess)
WIIUSE_ERROR("Unable to close channel [id %i].", wm ? wm->unid : -1);
[*pChannel release];
*pChannel = nil;
}
- (void) disconnect {
// channels
[self disconnectChannel:&interruptChannel];
[self disconnectChannel:&controlChannel];
// device
if([device closeConnection] != kIOReturnSuccess)
WIIUSE_ERROR("Unable to close the device connection [id %i].", wm ? wm->unid : -1);
[device release];
device = nil;
}
- (void) disconnected:(IOBluetoothUserNotification*) notification fromDevice:(IOBluetoothDevice*) device {
WiiuseDisconnectionMessage* message = [[WiiuseDisconnectionMessage alloc] init];
[receivedDataLock lock];
[receivedData addObject:message];
[receivedDataLock unlock];
[message release];
}
#pragma mark read, write
// <0: nothing received, else: length of data received (can be 0 in case of disconnection message)
- (int) checkForAvailableDataForBuffer: (byte*) buffer length: (NSUInteger) bufferLength {
int result = -1;
[receivedDataLock lock];
if([receivedData count]) {
// look at first item in queue
NSObject<WiiuseReceivedMessage>* firstMessage = [receivedData objectAtIndex:0];
result = [firstMessage applyToStruct:wm buffer: buffer length: bufferLength];
if(result >= 0)
[receivedData removeObjectAtIndex:0];
}
[receivedDataLock unlock];
return result;
}
- (void) waitForIncomingData: (NSTimeInterval) duration {
NSDate* timeoutDate = [NSDate dateWithTimeIntervalSinceNow: duration];
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (true) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // This is used for fast release of NSDate, otherwise it leaks
if(![theRL runMode:NSDefaultRunLoopMode beforeDate:timeoutDate]) {
WIIUSE_ERROR("Could not start run loop while waiting for read [id %i].", wm->unid);
break;
}
[pool drain];
[receivedDataLock lock];
NSUInteger count = [receivedData count];
[receivedDataLock unlock];
if(count) {
// received some data, stop waiting
break;
}
if([timeoutDate isLessThanOrEqualTo:[NSDate date]]) {
// timeout
break;
}
}
}
// result = length of data copied to event buffer
- (int) readBuffer:(byte *)buffer length:(NSUInteger)bufferLength {
// is there already some data to read?
int result = [self checkForAvailableDataForBuffer: buffer length: bufferLength];
if(result < 0) {
// wait a short amount of time, until data becomes available or a timeout is reached
[self waitForIncomingData:1];
// check again
result = [self checkForAvailableDataForBuffer: buffer length: bufferLength];
}
return result >= 0 ? result : 0;
}
- (int) writeReport: (byte) report_type buffer: (byte*) buffer length: (NSUInteger) length {
if(interruptChannel == nil) {
WIIUSE_ERROR("Attempted to write to nil interrupt channel [id %i].", wm->unid);
return 0;
}
byte write_buffer[MAX_PAYLOAD];
write_buffer[0] = WM_SET_DATA | WM_BT_OUTPUT;
write_buffer[1] = report_type;
memcpy(write_buffer+2, buffer, length);
IOReturn error = [interruptChannel writeSync:write_buffer length:length+2];
if (error != kIOReturnSuccess) {
WIIUSE_ERROR("Error writing to interrupt channel [id %i].", wm->unid);
WIIUSE_DEBUG("Attempting to reopen the interrupt channel [id %i].", wm->unid);
[self disconnectChannel:&interruptChannel];
[self connectChannel:&interruptChannel PSM:kBluetoothL2CAPPSMHIDInterrupt];
if(!interruptChannel) {
WIIUSE_ERROR("Error reopening the interrupt channel [id %i].", wm->unid);
[self disconnect];
} else {
WIIUSE_DEBUG("Attempting to write again to the interrupt channel [id %i].", wm->unid);
error = [interruptChannel writeSync:write_buffer length:length+2];
if (error != kIOReturnSuccess)
WIIUSE_ERROR("Unable to write again to the interrupt channel [id %i].", wm->unid);
}
}
return (error == kIOReturnSuccess) ? length : 0;
}
#pragma mark IOBluetoothL2CAPChannelDelegate
- (void) l2capChannelData:(IOBluetoothL2CAPChannel*)channel data:(void*)data_ length:(NSUInteger)length {
byte* data = (byte*) data_;
// This is done in case the control channel woke up this handler
#if WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE
BluetoothL2CAPPSM psm = channel.PSM;
#else
BluetoothL2CAPPSM psm = [channel getPSM];
#endif
if(!data || (psm == kBluetoothL2CAPPSMHIDControl)) {
return;
}
// copy the data into the buffer
// on Mac, we ignore the first byte
WiiuseReceivedData* newData = [[WiiuseReceivedData alloc] initWithBytes: data+1 length: length-1];
[receivedDataLock lock];
[receivedData addObject: newData];
[receivedDataLock unlock];
[newData release];
}
#if !WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE
// the following delegate methods were required on 10.6. They are here to get rid of 10.6 compiler warnings.
- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel status:(IOReturn)error {
/* no-op */
}
- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel {
/* no-op */
}
- (void)l2capChannelReconfigured:(IOBluetoothL2CAPChannel*)l2capChannel {
/* no-op */
}
- (void)l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*)l2capChannel refcon:(void*)refcon status:(IOReturn)error {
/* no-op */
}
- (void)l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*)l2capChannel {
/* no-op */
}
#endif
@end
#pragma mark -
#pragma mark WiiuseReceivedMessage
@implementation WiiuseReceivedData
- (id) initWithData:(NSData *)data_ {
self = [super init];
if (self) {
data = [data_ retain];
}
return self;
}
- (id) initWithBytes: (void*) bytes length: (NSUInteger) length {
NSData* data_ = [[NSData alloc] initWithBytes:bytes length:length];
id result = [self initWithData: data_];
[data_ release];
return result;
}
- (void) dealloc {
[data release];
[super dealloc];
}
- (int) applyToStruct:(wiimote *)wm buffer:(byte *)buffer length:(NSUInteger)bufferLength {
byte* bytes = (byte*) [data bytes];
NSUInteger length = [data length];
if(length > bufferLength) {
WIIUSE_WARNING("Received data was longer than event buffer. Dropping excess bytes.");
length = bufferLength;
}
// log the received data
#ifdef WITH_WIIUSE_DEBUG
{
printf("[DEBUG] (id %i) RECV: (%.2x) ", wm->unid, bytes[0]);
int x;
for (x = 1; x < length; ++x)
printf("%.2x ", bytes[x]);
printf("\n");
}
#endif
// copy to struct
memcpy(buffer, bytes, length);
return length;
}
@end
@implementation WiiuseDisconnectionMessage
- (int) applyToStruct:(wiimote *)wm buffer:(byte *)buffer length:(NSUInteger)bufferLength {
wiiuse_disconnected(wm);
return 0;
}
@end
#endif // __APPLE__

View File

@@ -0,0 +1,240 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 OS X.
*/
#ifdef __APPLE__
#import "os_mac.h"
#import "../io.h"
#import "../events.h"
#import "../os.h"
#import <IOBluetooth/IOBluetoothUtilities.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#if !WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE
#import <IOBluetooth/IOBluetoothUserLib.h> // IOBluetoothLocalDeviceGetPowerState
#endif
#pragma mark -
#pragma mark WiiuseDeviceInquiry
#if WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE
@interface WiiuseDeviceInquiry : NSObject<IOBluetoothDeviceInquiryDelegate> {
#else
@interface WiiuseDeviceInquiry : NSObject {
#endif
wiimote** wiimotes;
NSUInteger maxDevices;
int timeout;
BOOL _running;
NSUInteger _foundDevices;
BOOL _inquiryComplete;
}
- (id) initWithMemory:(wiimote**)wiimotes maxDevices:(int)maxDevices timeout:(int)timeout;
- (int) run;
@end
@implementation WiiuseDeviceInquiry
- (id) initWithMemory:(wiimote**)wiimotes_ maxDevices:(int)maxDevices_ timeout:(int)timeout_ {
self = [super init];
if(self) {
BluetoothHCIPowerState powerState = kBluetoothHCIPowerStateUnintialized;
#if WIIUSE_MAC_OS_X_VERSION_10_7_OR_ABOVE
if([IOBluetoothHostController defaultController])
powerState = [IOBluetoothHostController defaultController].powerState;
#else
// yes it is deprecated. no, there is no alternative (on 10.6).
IOBluetoothLocalDeviceGetPowerState(&powerState);
#endif
if (![IOBluetoothHostController defaultController] ||
powerState != kBluetoothHCIPowerStateON)
{
WIIUSE_DEBUG("Bluetooth hardware not available.");
[self release];
self = nil;
} else {
wiimotes = wiimotes_;
maxDevices = maxDevices_;
timeout = timeout_;
_running = NO;
}
}
return self;
}
// creates and starts inquiry. the returned object is in the current autorelease pool.
- (IOBluetoothDeviceInquiry*) start {
// reset state variables
_foundDevices = 0;
_inquiryComplete = NO;
// create inquiry
IOBluetoothDeviceInquiry* inquiry = [IOBluetoothDeviceInquiry inquiryWithDelegate: self];
// refine search & set timeout
[inquiry setSearchCriteria:kBluetoothServiceClassMajorAny
majorDeviceClass:WM_DEV_MAJOR_CLASS
minorDeviceClass:kBluetoothDeviceClassMinorAny];
[inquiry setUpdateNewDeviceNames: NO];
if(timeout > 0)
[inquiry setInquiryLength:timeout];
// start inquiry
IOReturn status = [inquiry start];
if (status != kIOReturnSuccess) {
WIIUSE_ERROR("Unable to start bluetooth device inquiry.");
if(![inquiry stop]) {
WIIUSE_ERROR("Unable to stop bluetooth device inquiry.");
} else {
WIIUSE_DEBUG("Bluetooth device inquiry stopped.");
}
return nil;
}
return inquiry;
}
- (void) wait {
// wait for the inquiry to complete
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while(!_inquiryComplete &&
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
// no-op
}
}
- (NSUInteger) collectResultsOf: (IOBluetoothDeviceInquiry*) inquiry {
// stop the inquiry
if(![inquiry stop])
WIIUSE_ERROR("Unable to stop bluetooth device inquiry.");
// read found device information
NSArray* devices = [inquiry foundDevices];
NSUInteger i;
for(i = 0; i < [devices count]; i++) {
IOBluetoothDevice* device = [devices objectAtIndex:i];
// save the device in the wiimote structure
wiimotes[i]->objc_wm = (void*) [[WiiuseWiimote alloc] initWithPtr:wiimotes[i] device: device];
// mark as found
WIIMOTE_ENABLE_STATE(wiimotes[i], WIIMOTE_STATE_DEV_FOUND);
NSString* address = IOBluetoothNSStringFromDeviceAddress([device getAddress]);
const char* address_str = [address cStringUsingEncoding:NSMacOSRomanStringEncoding];
WIIUSE_INFO("Found Wiimote (%s) [id %i]", address_str, wiimotes[i]->unid);
}
return [devices count];
}
- (int) run {
int result = -1;
if(maxDevices == 0) {
result = 0;
} else if(!_running) {
_running = YES;
if (![IOBluetoothHostController defaultController]) {
WIIUSE_ERROR("Unable to find any bluetooth receiver on your host.");
} else {
IOBluetoothDeviceInquiry* inquiry = [self start];
if(inquiry) {
[self wait];
result = [self collectResultsOf: inquiry];
WIIUSE_INFO("Found %i Wiimote device(s).", result);
}
}
_running = NO;
} else { // if(_running)
WIIUSE_ERROR("Device inquiry already running - won't start it again.");
}
return result;
}
#pragma mark IOBluetoothDeviceInquiryDelegate
- (void) deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *) inquiry device:(IOBluetoothDevice *) device {
WIIUSE_DEBUG("Found a wiimote");
_foundDevices++;
if(_foundDevices >= maxDevices) {
// reached maximum number of devices
_inquiryComplete = YES;
}
}
- (void) deviceInquiryComplete:(IOBluetoothDeviceInquiry*) inquiry error:(IOReturn) error aborted:(BOOL) aborted
{
WIIUSE_DEBUG("Inquiry complete, error=%i, aborted=%s", error, aborted ? "YES" : "NO");
// mark completion
_inquiryComplete = YES;
// print error message if we stop due to an error
if ((error != kIOReturnSuccess) && !aborted) {
WIIUSE_ERROR("Bluetooth device inquiry not completed due to unexpected errors. Try increasing the timeout.");
}
}
@end
#pragma mark -
#pragma mark public interface
int wiiuse_os_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
int result;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WiiuseDeviceInquiry* inquiry = [[WiiuseDeviceInquiry alloc] initWithMemory:wm maxDevices:max_wiimotes timeout:timeout];
result = [inquiry run];
[inquiry release];
[pool drain];
return result;
}
#endif // __APPLE__

View File

@@ -0,0 +1,222 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 C function interface to os_mac.
*/
#ifdef __APPLE__
#import "os_mac.h"
#import "../io.h"
#import "../events.h"
#import "../os.h"
#import <IOBluetooth/IOBluetoothUtilities.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#import <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
#pragma mark -
#pragma mark find
// See os_mac_find.m
#pragma mark -
#pragma mark connect, disconnect
/**
* @brief Connect to a wiimote with a known address.
*
* @param wm Pointer to a wiimote_t structure.
*
* @see wiimote_os_connect()
* @see wiimote_os_find()
*
* @return 1 on success, 0 on failure
*/
static short wiiuse_os_connect_single(struct wiimote_t* wm) {
// Skip if already connected or device not found
if(!wm) {
WIIUSE_ERROR("No Wiimote given.");
return 0;
} else if(wm && (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_DEV_FOUND) || wm->objc_wm == NULL)) {
WIIUSE_ERROR("Tried to connect Wiimote without an address.");
return 0;
} else if(WIIMOTE_IS_CONNECTED(wm)) {
WIIUSE_WARNING("Wiimote [id %i] is already connected.", wm->unid);
return 1;
}
WIIUSE_DEBUG("Connecting to Wiimote [id %i].", wm->unid);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
short result = 0;
// connect
WiiuseWiimote* objc_wm = (WiiuseWiimote*) wm->objc_wm;
if([objc_wm connect] == kIOReturnSuccess) {
WIIUSE_INFO("Connected to Wiimote [id %i].", wm->unid);
// save the connect structure to retrieve data later on
wm->objc_wm = (void*)objc_wm;
// save the connection status
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
// Do the handshake
wiiuse_handshake(wm, NULL, 0);
wiiuse_set_report_type(wm);
result = 1;
}
[pool drain];
return result;
}
int wiiuse_os_connect(struct wiimote_t** wm, int wiimotes) {
int connected = 0;
int i;
for (i = 0; i < wiimotes; ++i) {
if(wm[i] == NULL) {
WIIUSE_ERROR("Trying to connect to non-initialized Wiimote.");
break;
}
if (!WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_DEV_FOUND) || !wm[i]->objc_wm) {
// If the device is not found, skip it
continue;
}
if (wiiuse_os_connect_single(wm[i]))
++connected;
}
return connected;
}
void wiiuse_os_disconnect(struct wiimote_t* wm) {
if (!wm || !WIIMOTE_IS_CONNECTED(wm) || !wm->objc_wm)
return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[((WiiuseWiimote*)wm->objc_wm) disconnect];
[pool drain];
}
#pragma mark -
#pragma mark poll, read, write
int wiiuse_os_poll(struct wiimote_t** wm, int wiimotes) {
int i;
byte read_buffer[MAX_PAYLOAD];
int evnt = 0;
if (!wm) return 0;
for (i = 0; i < wiimotes; ++i) {
wm[i]->event = WIIUSE_NONE;
/* clear out the buffer */
memset(read_buffer, 0, sizeof(read_buffer));
/* read */
if (wiiuse_os_read(wm[i], read_buffer, sizeof(read_buffer))) {
/* propagate the event */
propagate_event(wm[i], read_buffer[0], read_buffer+1);
} else {
/* send out any waiting writes */
wiiuse_send_next_pending_write_request(wm[i]);
idle_cycle(wm[i]);
}
evnt += (wm[i]->event != WIIUSE_NONE);
}
return evnt;
}
int wiiuse_os_read(struct wiimote_t* wm, byte* buf, int len) {
if(!wm || !wm->objc_wm) return 0;
if(!WIIMOTE_IS_CONNECTED(wm)) {
WIIUSE_ERROR("Attempting to read from unconnected Wiimote");
return 0;
}
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WiiuseWiimote* objc_wm = (WiiuseWiimote*) wm->objc_wm;
int result = [objc_wm readBuffer: buf length: len];
[pool drain];
return result;
}
int wiiuse_os_write(struct wiimote_t* wm, byte report_type, byte* buf, int len) {
if(!wm || !wm->objc_wm) return 0;
if(!WIIMOTE_IS_CONNECTED(wm)) {
WIIUSE_ERROR("Attempting to write to unconnected Wiimote");
return 0;
}
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
WiiuseWiimote* objc_wm = (WiiuseWiimote*) wm->objc_wm;
int result = [objc_wm writeReport: report_type buffer: buf length: (NSUInteger)len];
[pool drain];
return result;
}
#pragma mark -
#pragma mark platform fields
void wiiuse_init_platform_fields(struct wiimote_t* wm) {
wm->objc_wm = NULL;
}
void wiiuse_cleanup_platform_fields(struct wiimote_t* wm) {
if(!wm) return;
WiiuseWiimote* objc_wm = (WiiuseWiimote*) wm->objc_wm;
// disconnect
// Note: this should already have happened, because this function
// is called once the device is disconnected. This is just paranoia.
[objc_wm disconnect];
// release WiiuseWiimote object
[objc_wm release];
wm->objc_wm = NULL;
}
#endif // __APPLE__

396
src/main/c/os_nix.c Normal file
View File

@@ -0,0 +1,396 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 *nix.
*/
#include "wiiuse_internal.h" /* for WM_RPT_CTRL_STATUS */
#include "io.h"
#include "events.h"
#include "os.h"
#ifdef WIIUSE_BLUEZ
#include <bluetooth/bluetooth.h> /* for ba2str, str2ba */
#include <bluetooth/hci.h> /* for inquiry_info */
#include <bluetooth/hci_lib.h> /* for hci_get_route, hci_inquiry, etc */
#include <bluetooth/l2cap.h> /* for sockaddr_l2 */
#include <stdio.h> /* for perror */
#include <string.h> /* for memset */
#include <sys/socket.h> /* for connect, socket */
#include <sys/time.h> /* for struct timeval */
#include <unistd.h> /* for close, write */
#include <errno.h>
static int wiiuse_os_connect_single(struct wiimote_t* wm, char* address);
int wiiuse_os_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
int device_id;
int device_sock;
inquiry_info scan_info_arr[128];
inquiry_info* scan_info = scan_info_arr;
int found_devices;
int found_wiimotes;
int i = 0;
/* reset all wiimote bluetooth device addresses */
for (found_wiimotes = 0; found_wiimotes < max_wiimotes; ++found_wiimotes) {
/* bacpy(&(wm[found_wiimotes]->bdaddr), BDADDR_ANY); */
memset(&(wm[found_wiimotes]->bdaddr), 0, sizeof(bdaddr_t));
}
found_wiimotes = 0;
/* get the id of the first bluetooth device. */
device_id = hci_get_route(NULL);
if (device_id < 0) {
if (errno == ENODEV) {
WIIUSE_ERROR("Could not detect a Bluetooth adapter!");
} else {
perror("hci_get_route");
}
return 0;
}
/* create a socket to the device */
device_sock = hci_open_dev(device_id);
if (device_sock < 0) {
perror("hci_open_dev");
return 0;
}
memset(&scan_info_arr, 0, sizeof(scan_info_arr));
/* scan for bluetooth devices for 'timeout' seconds */
found_devices = hci_inquiry(device_id, timeout, 128, NULL, &scan_info, IREQ_CACHE_FLUSH);
if (found_devices < 0) {
perror("hci_inquiry");
close(device_sock);
return 0;
}
WIIUSE_INFO("Found %i bluetooth device(s).", found_devices);
/* display discovered devices */
for (i = 0; (i < found_devices) && (found_wiimotes < max_wiimotes); ++i) {
if ((scan_info[i].dev_class[0] == WM_DEV_CLASS_0) &&
(scan_info[i].dev_class[1] == WM_DEV_CLASS_1) &&
(scan_info[i].dev_class[2] == WM_DEV_CLASS_2)) {
/* found a device */
ba2str(&scan_info[i].bdaddr, wm[found_wiimotes]->bdaddr_str);
WIIUSE_INFO("Found wiimote (%s) [id %i].", wm[found_wiimotes]->bdaddr_str, wm[found_wiimotes]->unid);
wm[found_wiimotes]->bdaddr = scan_info[i].bdaddr;
WIIMOTE_ENABLE_STATE(wm[found_wiimotes], WIIMOTE_STATE_DEV_FOUND);
++found_wiimotes;
}
}
close(device_sock);
return found_wiimotes;
}
/**
* @see wiiuse_connect()
* @see wiiuse_os_connect_single()
*/
int wiiuse_os_connect(struct wiimote_t** wm, int wiimotes) {
int connected = 0;
int i = 0;
for (; i < wiimotes; ++i) {
if (!WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_DEV_FOUND))
/* if the device address is not set, skip it */
{
continue;
}
if (wiiuse_os_connect_single(wm[i], NULL)) {
++connected;
}
}
return connected;
}
/**
* @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 wiiuse_os_find().
*
* @return 1 on success, 0 on failure
*/
static int wiiuse_os_connect_single(struct wiimote_t* wm, char* address) {
struct sockaddr_l2 addr;
memset(&addr, 0, sizeof(addr));
if (!wm || WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
addr.l2_family = AF_BLUETOOTH;
bdaddr_t *bdaddr = &wm->bdaddr;
if (address)
/* use provided address */
{
str2ba(address, &addr.l2_bdaddr);
} else {
/** @todo this line doesn't make sense
bacmp(bdaddr, BDADDR_ANY);*/
/* use address of device discovered */
addr.l2_bdaddr = *bdaddr;
}
/*
* OUTPUT CHANNEL
*/
wm->out_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (wm->out_sock == -1) {
return 0;
}
addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
/* connect to wiimote */
if (connect(wm->out_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("connect() output sock");
return 0;
}
/*
* INPUT CHANNEL
*/
wm->in_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (wm->in_sock == -1) {
close(wm->out_sock);
wm->out_sock = -1;
return 0;
}
addr.l2_psm = htobs(WM_INPUT_CHANNEL);
/* connect to wiimote */
if (connect(wm->in_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("connect() interrupt sock");
close(wm->out_sock);
wm->out_sock = -1;
return 0;
}
WIIUSE_INFO("Connected to wiimote [id %i].", wm->unid);
/* do the handshake */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
wiiuse_handshake(wm, NULL, 0);
wiiuse_set_report_type(wm);
return 1;
}
void wiiuse_os_disconnect(struct wiimote_t* wm) {
if (!wm || WIIMOTE_IS_CONNECTED(wm)) {
return;
}
close(wm->out_sock);
close(wm->in_sock);
wm->out_sock = -1;
wm->in_sock = -1;
wm->event = WIIUSE_NONE;
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
}
int wiiuse_os_poll(struct wiimote_t** wm, int wiimotes) {
int evnt;
struct timeval tv;
fd_set fds;
int r;
int i;
byte read_buffer[MAX_PAYLOAD];
int highest_fd = -1;
evnt = 0;
if (!wm) {
return 0;
}
/* block select() for 1/2000th of a second */
tv.tv_sec = 0;
tv.tv_usec = 500;
FD_ZERO(&fds);
for (i = 0; i < wiimotes; ++i) {
/* only poll it if it is connected */
if (WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_CONNECTED)) {
FD_SET(wm[i]->in_sock, &fds);
/* find the highest fd of the connected wiimotes */
if (wm[i]->in_sock > highest_fd) {
highest_fd = wm[i]->in_sock;
}
}
wm[i]->event = WIIUSE_NONE;
}
if (highest_fd == -1)
/* nothing to poll */
{
return 0;
}
if (select(highest_fd + 1, &fds, NULL, NULL, &tv) == -1) {
WIIUSE_ERROR("Unable to select() the wiimote interrupt socket(s).");
perror("Error Details");
return 0;
}
/* check each socket for an event */
for (i = 0; i < wiimotes; ++i) {
/* if this wiimote is not connected, skip it */
if (!WIIMOTE_IS_CONNECTED(wm[i])) {
continue;
}
if (FD_ISSET(wm[i]->in_sock, &fds)) {
/* clear out the event buffer */
memset(read_buffer, 0, sizeof(read_buffer));
/* clear out any old read data */
clear_dirty_reads(wm[i]);
/* read the pending message into the buffer */
r = wiiuse_os_read(wm[i], read_buffer, sizeof(read_buffer));
if (r > 0) {
/* propagate the event */
propagate_event(wm[i], read_buffer[0], read_buffer + 1);
evnt += (wm[i]->event != WIIUSE_NONE);
} else if (!WIIMOTE_IS_CONNECTED(wm[i])) {
/* freshly disconnected */
wm[i]->event = (r==0) ? WIIUSE_DISCONNECT : WIIUSE_UNEXPECTED_DISCONNECT;
evnt++;
/* propagate the event:
Emit a controller-status type event. */
propagate_event(wm[i], WM_RPT_CTRL_STATUS, 0);
}
} else {
/* send out any waiting writes */
wiiuse_send_next_pending_write_request(wm[i]);
idle_cycle(wm[i]);
}
}
return evnt;
}
int wiiuse_os_read(struct wiimote_t* wm, byte* buf, int len) {
int rc;
int i;
rc = read(wm->in_sock, buf, len);
if (rc == -1) {
/* error reading data */
WIIUSE_ERROR("Receiving wiimote data (id %i).", wm->unid);
perror("Error Details");
if (errno == ENOTCONN) {
/* this can happen if the bluetooth dongle is disconnected */
WIIUSE_ERROR("Bluetooth appears to be disconnected. Wiimote unid %i will be disconnected.", wm->unid);
wiiuse_os_disconnect(wm);
wiiuse_disconnected(wm);
}
} else if (rc == 0) {
/* remote disconnect */
wiiuse_disconnected(wm);
} else {
/* read successful */
/* on *nix we ignore the first byte */
memmove(buf, buf + 1, len - 1);
/* log the received data */
#ifdef WITH_WIIUSE_DEBUG
{
int i;
printf("[DEBUG] (id %i) RECV: (%.2x) ", wm->unid, buf[0]);
for (i = 1; i < rc; i++) {
printf("%.2x ", buf[i]);
}
printf("\n");
}
#endif
}
return rc;
}
int wiiuse_os_write(struct wiimote_t* wm, byte report_type, byte* buf, int len) {
int rc;
byte write_buffer[MAX_PAYLOAD];
write_buffer[0] = WM_SET_REPORT | WM_BT_OUTPUT;
write_buffer[1] = report_type;
memcpy(write_buffer + 2, buf, len);
rc = write(wm->out_sock, write_buffer, len + 2);
if (rc < 0) {
wiiuse_disconnected(wm);
}
return rc;
}
void wiiuse_init_platform_fields(struct wiimote_t* wm) {
memset(&(wm->bdaddr), 0, sizeof(bdaddr_t)); /* = *BDADDR_ANY;*/
wm->out_sock = -1;
wm->in_sock = -1;
}
void wiiuse_cleanup_platform_fields(struct wiimote_t* wm) {
wm->out_sock = -1;
wm->in_sock = -1;
}
#endif /* ifdef WIIUSE_BLUEZ */

327
src/main/c/os_win.c Normal file
View File

@@ -0,0 +1,327 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Windows.
*/
#include "io.h"
#include "events.h"
#include "os.h"
#ifdef WIIUSE_WIN32
#include <stdlib.h>
#include <hidsdi.h>
#include <setupapi.h>
#ifdef __MINGW32__
/* this prototype is missing from the mingw headers so we must add it
or suffer linker errors. */
# ifdef __cplusplus
extern "C" {
# endif
WIIUSE_EXPORT BOOL WINAPI HidD_SetOutputReport(HANDLE, PVOID, ULONG);
# ifdef __cplusplus
}
# endif
#endif
int wiiuse_os_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
GUID device_id;
HANDLE dev;
HDEVINFO device_info;
int i, index;
DWORD len;
SP_DEVICE_INTERFACE_DATA device_data;
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
HIDD_ATTRIBUTES attr;
int found = 0;
(void) timeout; /* unused */
device_data.cbSize = sizeof(device_data);
index = 0;
/* get the device id */
HidD_GetHidGuid(&device_id);
/* get all hid devices connected */
device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
for (;; ++index) {
if (detail_data) {
free(detail_data);
detail_data = NULL;
}
/* query the next hid device info */
if (!SetupDiEnumDeviceInterfaces(device_info, NULL, &device_id, index, &device_data)) {
break;
}
/* get the size of the data block required */
i = SetupDiGetDeviceInterfaceDetail(device_info, &device_data, NULL, 0, &len, NULL);
detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*)malloc(len);
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
/* query the data for this device */
if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL)) {
continue;
}
/* open the device */
dev = CreateFile(detail_data->DevicePath,
(GENERIC_READ | GENERIC_WRITE),
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (dev == INVALID_HANDLE_VALUE) {
continue;
}
/* get device attributes */
attr.Size = sizeof(attr);
i = HidD_GetAttributes(dev, &attr);
if ((attr.VendorID == WM_VENDOR_ID) && (attr.ProductID == WM_PRODUCT_ID)) {
/* this is a wiimote */
wm[found]->dev_handle = dev;
wm[found]->hid_overlap.hEvent = CreateEvent(NULL, 1, 1, "");
wm[found]->hid_overlap.Offset = 0;
wm[found]->hid_overlap.OffsetHigh = 0;
WIIMOTE_ENABLE_STATE(wm[found], WIIMOTE_STATE_DEV_FOUND);
WIIMOTE_ENABLE_STATE(wm[found], WIIMOTE_STATE_CONNECTED);
/* try to set the output report to see if the device is actually connected */
if (!wiiuse_set_report_type(wm[found])) {
WIIMOTE_DISABLE_STATE(wm[found], WIIMOTE_STATE_CONNECTED);
continue;
}
/* do the handshake */
wiiuse_handshake(wm[found], NULL, 0);
WIIUSE_INFO("Connected to wiimote [id %i].", wm[found]->unid);
++found;
if (found >= max_wiimotes) {
break;
}
} else {
/* not a wiimote */
CloseHandle(dev);
}
}
if (detail_data) {
free(detail_data);
}
SetupDiDestroyDeviceInfoList(device_info);
return found;
}
int wiiuse_os_connect(struct wiimote_t** wm, int wiimotes) {
int connected = 0;
int i = 0;
for (; i < wiimotes; ++i) {
if (!wm[i]) {
continue;
}
if (WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_CONNECTED)) {
++connected;
}
}
return connected;
}
void wiiuse_os_disconnect(struct wiimote_t* wm) {
if (!wm || WIIMOTE_IS_CONNECTED(wm)) {
return;
}
CloseHandle(wm->dev_handle);
wm->dev_handle = 0;
ResetEvent(&wm->hid_overlap);
wm->event = WIIUSE_NONE;
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
}
int wiiuse_os_poll(struct wiimote_t** wm, int wiimotes) {
int i;
byte read_buffer[MAX_PAYLOAD];
int evnt = 0;
if (!wm) {
return 0;
}
for (i = 0; i < wiimotes; ++i) {
wm[i]->event = WIIUSE_NONE;
/* clear out the buffer */
memset(read_buffer, 0, sizeof(read_buffer));
/* read */
if (wiiuse_os_read(wm[i], read_buffer, sizeof(read_buffer))) {
/* propagate the event */
propagate_event(wm[i], read_buffer[0], read_buffer + 1);
evnt += (wm[i]->event != WIIUSE_NONE);
} else {
/* send out any waiting writes */
wiiuse_send_next_pending_write_request(wm[i]);
idle_cycle(wm[i]);
}
}
return evnt;
}
int wiiuse_os_read(struct wiimote_t* wm, byte* buf, int len) {
DWORD b, r;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
if (!ReadFile(wm->dev_handle, buf, len, &b, &wm->hid_overlap)) {
/* partial read */
b = GetLastError();
if ((b == ERROR_HANDLE_EOF) || (b == ERROR_DEVICE_NOT_CONNECTED)) {
/* remote disconnect */
wiiuse_disconnected(wm);
return 0;
}
r = WaitForSingleObject(wm->hid_overlap.hEvent, wm->timeout);
if (r == WAIT_TIMEOUT) {
/* timeout - cancel and continue */
if (*buf) {
WIIUSE_WARNING("Packet ignored. This may indicate a problem (timeout is %i ms).", wm->timeout);
}
CancelIo(wm->dev_handle);
ResetEvent(wm->hid_overlap.hEvent);
return 0;
} else if (r == WAIT_FAILED) {
WIIUSE_WARNING("A wait error occurred on reading from wiimote %i.", wm->unid);
return 0;
}
if (!GetOverlappedResult(wm->dev_handle, &wm->hid_overlap, &b, 0)) {
return 0;
}
/* log the received data */
#ifdef WITH_WIIUSE_DEBUG
{
DWORD i;
printf("[DEBUG] (id %i) RECV: (%.2x) ", wm->unid, buf[0]);
for (i = 1; i < b; i++) {
printf("%.2x ", buf[i]);
}
printf("\n");
}
#endif
}
ResetEvent(wm->hid_overlap.hEvent);
return 1;
}
int wiiuse_os_write(struct wiimote_t* wm, byte report_type, byte* buf, int len) {
DWORD bytes;
int i;
byte write_buffer[MAX_PAYLOAD];
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
write_buffer[0] = report_type;
memcpy(write_buffer + 1, buf, len);
switch (wm->stack) {
case WIIUSE_STACK_UNKNOWN: {
/* try to auto-detect the stack type */
if (i = WriteFile(wm->dev_handle, write_buffer, 22, &bytes, &wm->hid_overlap)) {
/* bluesoleil will always return 1 here, even if it's not connected */
wm->stack = WIIUSE_STACK_BLUESOLEIL;
return i;
}
if (i = HidD_SetOutputReport(wm->dev_handle, write_buffer, len + 1)) {
wm->stack = WIIUSE_STACK_MS;
return i;
}
WIIUSE_ERROR("Unable to determine bluetooth stack type.");
return 0;
}
case WIIUSE_STACK_MS:
return HidD_SetOutputReport(wm->dev_handle, write_buffer, len + 1);
case WIIUSE_STACK_BLUESOLEIL:
return WriteFile(wm->dev_handle, write_buffer, 22, &bytes, &wm->hid_overlap);
}
return 0;
}
void wiiuse_init_platform_fields(struct wiimote_t* wm) {
wm->dev_handle = 0;
wm->stack = WIIUSE_STACK_UNKNOWN;
wm->normal_timeout = WIIMOTE_DEFAULT_TIMEOUT;
wm->exp_timeout = WIIMOTE_EXP_TIMEOUT;
wm->timeout = wm->normal_timeout;
}
void wiiuse_cleanup_platform_fields(struct wiimote_t* wm) {
wm->dev_handle = 0;
}
#endif /* ifdef WIIUSE_WIN32 */

49
src/main/c/util.c Normal file
View File

@@ -0,0 +1,49 @@
/*
* wiiuse
*
* Copyright 2011 Iowa State University Virtual Reality Applications Center
*
* 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 Provides platform-specific utility functions.
*/
#include "wiiuse_internal.h"
#ifdef WIIUSE_WIN32
#include <windows.h>
void wiiuse_millisleep(int durationMilliseconds) {
Sleep(durationMilliseconds);
}
#else /* not win32 - assuming posix */
#include <unistd.h> /* for usleep */
void wiiuse_millisleep(int durationMilliseconds) {
usleep(durationMilliseconds * 1000);
}
#endif /* ifdef WIIUSE_WIN32 */

146
src/main/c/wiiboard.c Normal file
View File

@@ -0,0 +1,146 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Wii Fit Balance Board device.
*/
#include "wiiboard.h"
#include <stdio.h> /* for printf */
#include <string.h> /* for memset */
/**
* @brief Handle the handshake data from the wiiboard.
*
* @param wb A pointer to a wii_board_t structure.
* @param data The data read in from the device.
* @param len The length of the data block, in bytes.
*
* @return Returns 1 if handshake was successful, 0 if not.
*/
int wii_board_handshake(struct wiimote_t* wm, struct wii_board_t* wb, byte* data, uint16_t len) {
byte * bufptr;
/* decrypt data */
#ifdef WITH_WIIUSE_DEBUG
int i;
printf("DECRYPTED DATA WIIBOARD\n");
for (i = 0; i < len; ++i) {
if (i % 16 == 0) {
if (i != 0) {
printf("\n");
}
printf("%X: ", 0x4a40000 + 32 + i);
}
printf("%02X ", data[i]);
}
printf("\n");
#endif
bufptr = data + 4;
wb->ctr[0] = unbuffer_big_endian_uint16_t(&bufptr);
wb->cbr[0] = unbuffer_big_endian_uint16_t(&bufptr);
wb->ctl[0] = unbuffer_big_endian_uint16_t(&bufptr);
wb->cbl[0] = unbuffer_big_endian_uint16_t(&bufptr);
wb->ctr[1] = unbuffer_big_endian_uint16_t(&bufptr);
wb->cbr[1] = unbuffer_big_endian_uint16_t(&bufptr);
wb->ctl[1] = unbuffer_big_endian_uint16_t(&bufptr);
wb->cbl[1] = unbuffer_big_endian_uint16_t(&bufptr);
wb->ctr[2] = unbuffer_big_endian_uint16_t(&bufptr);
wb->cbr[2] = unbuffer_big_endian_uint16_t(&bufptr);
wb->ctl[2] = unbuffer_big_endian_uint16_t(&bufptr);
wb->cbl[2] = unbuffer_big_endian_uint16_t(&bufptr);
/* handshake done */
wm->event = WIIUSE_WII_BOARD_CTRL_INSERTED;
wm->exp.type = EXP_WII_BOARD;
#ifdef WIIUSE_WIN32
wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
#endif
return 1;
}
/**
* @brief The wii board disconnected.
*
* @param cc A pointer to a wii_board_t structure.
*/
void wii_board_disconnected(struct wii_board_t* wb) {
memset(wb, 0, sizeof(struct wii_board_t));
}
static float do_interpolate(uint16_t raw, uint16_t cal[3]) {
#define WIIBOARD_MIDDLE_CALIB 17.0f
if (raw < cal[0]) {
return 0.0f;
} else if (raw < cal[1]) {
return ((float)(raw - cal[0]) * WIIBOARD_MIDDLE_CALIB) / (float)(cal[1] - cal[0]);
} else if (raw < cal[2]) {
return ((float)(raw - cal[1]) * WIIBOARD_MIDDLE_CALIB) / (float)(cal[2] - cal[1]) + WIIBOARD_MIDDLE_CALIB;
} else {
return WIIBOARD_MIDDLE_CALIB * 2.0f;
}
}
/**
* @brief Handle wii board event.
*
* @param wb A pointer to a wii_board_t structure.
* @param msg The message specified in the event packet.
*/
void wii_board_event(struct wii_board_t* wb, byte* msg) {
byte * bufPtr = msg;
wb->rtr = unbuffer_big_endian_uint16_t(&bufPtr);
wb->rbr = unbuffer_big_endian_uint16_t(&bufPtr);
wb->rtl = unbuffer_big_endian_uint16_t(&bufPtr);
wb->rbl = unbuffer_big_endian_uint16_t(&bufPtr);
/*
Interpolate values
Calculations borrowed from wiili.org - No names to mention sadly :( http://www.wiili.org/index.php/Wii_Balance_Board_PC_Drivers page however!
*/
wb->tr = do_interpolate(wb->rtr, wb->ctr);
wb->tl = do_interpolate(wb->rtl, wb->ctl);
wb->br = do_interpolate(wb->rbr, wb->cbr);
wb->bl = do_interpolate(wb->rbl, wb->cbl);
}
/**
@todo not implemented!
*/
void wiiuse_set_wii_board_calib(struct wiimote_t *wm) {
}

858
src/main/c/wiiuse.c Normal file
View File

@@ -0,0 +1,858 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 General wiimote operations.
*
* The file includes functions that handle general
* tasks. Most of these are functions that are part
* of the API.
*/
#include "wiiuse_internal.h"
#include "io.h" /* for wiiuse_handshake, etc */
#include "os.h" /* for wiiuse_os_* */
#include <stdio.h> /* for printf, FILE */
#include <stdlib.h> /* for malloc, free */
#include <string.h> /* for memcpy, memset */
static int g_banner = 0;
static const char g_wiiuse_version_string[] = WIIUSE_VERSION;
/**
* @brief Returns the version of the library.
*/
const char* wiiuse_version() {
return g_wiiuse_version_string;
}
/**
* @brief Output FILE stream for each wiiuse_loglevel.
*/
FILE* logtarget[4];
/**
* @brief Specify an alternate FILE stream for a log level.
*
* @param loglevel The loglevel, for which the output should be set.
*
* @param logfile A valid, writeable <code>FILE*</code>, or 0, if output should be disabled.
*
* The default <code>FILE*</code> for all loglevels is <code>stderr</code>
*/
void wiiuse_set_output(enum wiiuse_loglevel loglevel, FILE *logfile) {
logtarget[(int)loglevel] = logfile;
}
/**
* @brief Clean up wiimote_t array created by wiiuse_init()
*/
void wiiuse_cleanup(struct wiimote_t** wm, int wiimotes) {
int i = 0;
if (!wm) {
return;
}
WIIUSE_INFO("wiiuse clean up...");
for (; i < wiimotes; ++i) {
wiiuse_disconnect(wm[i]);
wiiuse_cleanup_platform_fields(wm[i]);
free(wm[i]);
}
free(wm);
return;
}
/**
* @brief Initialize an array of wiimote structures.
*
* @param wiimotes Number of wiimote_t structures to create.
*
* @return An array of initialized wiimote_t structures.
*
* @see wiiuse_connect()
*
* The array returned by this function can be passed to various
* functions, including wiiuse_connect().
*/
struct wiimote_t** wiiuse_init(int wiimotes) {
int i = 0;
struct wiimote_t** wm = NULL;
/*
* Please do not remove this banner.
* GPL asks that you please leave output credits intact.
* Thank you.
*
* This banner is only displayed once so that if you need
* to call this function again it won't be intrusive.
*/
if (!g_banner) {
printf("wiiuse v" WIIUSE_VERSION " loaded.\n"
" Fork at http://github.com/rpavlik/wiiuse\n"
" Original By: Michael Laforest <thepara[at]gmail{dot}com> http://wiiuse.net\n");
g_banner = 1;
}
logtarget[0] = stderr;
logtarget[1] = stderr;
logtarget[2] = stderr;
logtarget[3] = stderr;
if (!wiimotes) {
return NULL;
}
wm = malloc(sizeof(struct wiimote_t*) * wiimotes);
for (i = 0; i < wiimotes; ++i) {
wm[i] = malloc(sizeof(struct wiimote_t));
memset(wm[i], 0, sizeof(struct wiimote_t));
wm[i]->unid = i + 1;
wiiuse_init_platform_fields(wm[i]);
wm[i]->state = WIIMOTE_INIT_STATES;
wm[i]->flags = WIIUSE_INIT_FLAGS;
wm[i]->event = WIIUSE_NONE;
wm[i]->exp.type = EXP_NONE;
wm[i]->expansion_state = 0;
wiiuse_set_aspect_ratio(wm[i], WIIUSE_ASPECT_4_3);
wiiuse_set_ir_position(wm[i], WIIUSE_IR_ABOVE);
wm[i]->orient_threshold = 0.5f;
wm[i]->accel_threshold = 5;
wm[i]->accel_calib.st_alpha = WIIUSE_DEFAULT_SMOOTH_ALPHA;
}
return wm;
}
/**
* @brief The wiimote disconnected.
*
* @param wm Pointer to a wiimote_t structure.
*/
void wiiuse_disconnected(struct wiimote_t* wm) {
if (!wm) {
return;
}
WIIUSE_INFO("Wiimote disconnected [id %i].", wm->unid);
/* disable the connected flag */
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
/* reset a bunch of stuff */
wm->leds = 0;
wm->state = WIIMOTE_INIT_STATES;
wm->read_req = NULL;
#ifndef WIIUSE_SYNC_HANDSHAKE
wm->handshake_state = 0;
#endif
wm->btns = 0;
wm->btns_held = 0;
wm->btns_released = 0;
wm->event = WIIUSE_DISCONNECT;
}
/**
* @brief Enable or disable the rumble.
*
* @param wm Pointer to a wiimote_t structure.
* @param status 1 to enable, 0 to disable.
*/
void wiiuse_rumble(struct wiimote_t* wm, int status) {
byte buf;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return;
}
/* make sure to keep the current lit leds */
buf = wm->leds;
if (status) {
WIIUSE_DEBUG("Starting rumble...");
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_RUMBLE);
buf |= 0x01;
} else {
WIIUSE_DEBUG("Stopping rumble...");
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_RUMBLE);
buf &= ~(0x01);
}
/* preserve IR state */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
buf |= 0x04;
}
wiiuse_send(wm, WM_CMD_RUMBLE, &buf, 1);
}
/**
* @brief Toggle the state of the rumble.
*
* @param wm Pointer to a wiimote_t structure.
*/
void wiiuse_toggle_rumble(struct wiimote_t* wm) {
if (!wm) {
return;
}
wiiuse_rumble(wm, !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE));
}
/**
* @brief Set the enabled LEDs.
*
* @param wm Pointer to a wiimote_t structure.
* @param leds What LEDs to enable.
*
* \a leds is a bitwise or of WIIMOTE_LED_1, WIIMOTE_LED_2, WIIMOTE_LED_3, or WIIMOTE_LED_4.
*/
void wiiuse_set_leds(struct wiimote_t* wm, int leds) {
byte buf;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return;
}
/* remove the lower 4 bits because they control rumble */
wm->leds = (leds & 0xF0);
/* make sure if the rumble is on that we keep it on */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE)) {
wm->leds |= 0x01;
}
buf = wm->leds;
wiiuse_send(wm, WM_CMD_LED, &buf, 1);
}
/**
* @brief Set if the wiimote should report motion sensing.
*
* @param wm Pointer to a wiimote_t structure.
* @param status 1 to enable, 0 to disable.
*
* Since reporting motion sensing sends a lot of data,
* the wiimote saves power by not transmitting it
* by default.
*/
void wiiuse_motion_sensing(struct wiimote_t* wm, int status) {
if (status) {
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_ACC);
} else {
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_ACC);
}
wiiuse_set_report_type(wm);
}
/**
* @brief Set the report type based on the current wiimote state.
*
* @param wm Pointer to a wiimote_t structure.
*
* @return The report type sent.
*
* The wiimote reports formatted packets depending on the
* report type that was last requested. This function will
* update the type of report that should be sent based on
* the current state of the device.
*/
int wiiuse_set_report_type(struct wiimote_t* wm) {
byte buf[2];
int motion, exp, ir;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
buf[0] = (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_CONTINUOUS) ? 0x04 : 0x00); /* set to 0x04 for continuous reporting */
buf[1] = 0x00;
/* if rumble is enabled, make sure we keep it */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE)) {
buf[0] |= 0x01;
}
motion = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC);
exp = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP);
ir = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR);
if (motion && ir && exp) {
buf[1] = WM_RPT_BTN_ACC_IR_EXP;
} else if (motion && exp) {
buf[1] = WM_RPT_BTN_ACC_EXP;
} else if (motion && ir) {
buf[1] = WM_RPT_BTN_ACC_IR;
} else if (ir && exp) {
buf[1] = WM_RPT_BTN_IR_EXP;
} else if (ir) {
buf[1] = WM_RPT_BTN_ACC_IR;
} else if (exp) {
buf[1] = WM_RPT_BTN_EXP;
} else if (motion) {
buf[1] = WM_RPT_BTN_ACC;
} else {
buf[1] = WM_RPT_BTN;
}
WIIUSE_DEBUG("Setting report type: 0x%x", buf[1]);
exp = wiiuse_send(wm, WM_CMD_REPORT_TYPE, buf, 2);
if (exp <= 0) {
return exp;
}
return buf[1];
}
/**
* @brief Read data from the wiimote (callback version).
*
* @param wm Pointer to a wiimote_t structure.
* @param read_cb Function pointer to call when the data arrives from the wiimote.
* @param buffer An allocated buffer to store the data as it arrives from the wiimote.
* Must be persistent in memory and large enough to hold the data.
* @param addr The address of wiimote memory to read from.
* @param len The length of the block to be read.
*
* The library can only handle one data read request at a time
* because it must keep track of the buffer and other
* events that are specific to that request. So if a request
* has already been made, subsequent requests will be added
* to a pending list and be sent out when the previous
* finishes.
*/
int wiiuse_read_data_cb(struct wiimote_t* wm, wiiuse_read_cb read_cb, byte* buffer, unsigned int addr, uint16_t len) {
struct read_req_t* req;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
if (!buffer || !len) {
return 0;
}
/* make this request structure */
req = (struct read_req_t*)malloc(sizeof(struct read_req_t));
if (req == NULL) {
return 0;
}
req->cb = read_cb;
req->buf = buffer;
req->addr = addr;
req->size = len;
req->wait = len;
req->dirty = 0;
req->next = NULL;
/* add this to the request list */
if (!wm->read_req) {
/* root node */
wm->read_req = req;
WIIUSE_DEBUG("Data read request can be sent out immediately.");
/* send the request out immediately */
wiiuse_send_next_pending_read_request(wm);
} else {
struct read_req_t* nptr = wm->read_req;
for (; nptr->next; nptr = nptr->next) {
;
}
nptr->next = req;
WIIUSE_DEBUG("Added pending data read request.");
}
return 1;
}
/**
* @brief Read data from the wiimote (event version).
*
* @param wm Pointer to a wiimote_t structure.
* @param buffer An allocated buffer to store the data as it arrives from the wiimote.
* Must be persistent in memory and large enough to hold the data.
* @param addr The address of wiimote memory to read from.
* @param len The length of the block to be read.
*
* The library can only handle one data read request at a time
* because it must keep track of the buffer and other
* events that are specific to that request. So if a request
* has already been made, subsequent requests will be added
* to a pending list and be sent out when the previous
* finishes.
*/
int wiiuse_read_data(struct wiimote_t* wm, byte* buffer, unsigned int addr, uint16_t len) {
return wiiuse_read_data_cb(wm, NULL, buffer, addr, len);
}
/**
* @brief Send the next pending data read request to the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
*
* @see wiiuse_read_data()
*
* This function is not part of the wiiuse API.
*/
void wiiuse_send_next_pending_read_request(struct wiimote_t* wm) {
byte buf[6];
struct read_req_t* req;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return;
}
if (!wm->read_req) {
return;
}
/* skip over dirty ones since they have already been read */
req = wm->read_req;
while (req && req->dirty) {
req = req->next;
}
if (!req) {
return;
}
/* the offset is in big endian */
to_big_endian_uint32_t(buf, req->addr);
/* the length is in big endian */
to_big_endian_uint16_t(buf + 4, req->size);
WIIUSE_DEBUG("Request read at address: 0x%x length: %i", req->addr, req->size);
wiiuse_send(wm, WM_CMD_READ_DATA, buf, 6);
}
/**
* @brief Request the wiimote controller status.
*
* @param wm Pointer to a wiimote_t structure.
*
* Controller status includes: battery level, LED status, expansions
*/
void wiiuse_status(struct wiimote_t* wm) {
byte buf = 0;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return;
}
WIIUSE_DEBUG("Requested wiimote status.");
wiiuse_send(wm, WM_CMD_CTRL_STATUS, &buf, 1);
}
/**
* @brief Find a wiimote_t structure by its unique identifier.
*
* @param wm Pointer to a wiimote_t structure.
* @param wiimotes The number of wiimote_t structures in \a wm.
* @param unid The unique identifier to search for.
*
* @return Pointer to a wiimote_t structure, or NULL if not found.
*/
struct wiimote_t* wiiuse_get_by_id(struct wiimote_t** wm, int wiimotes, int unid) {
int i = 0;
if (!wm) {
return NULL;
}
for (; i < wiimotes; ++i) {
if (!wm[i]) {
continue;
}
if (wm[i]->unid == unid) {
return wm[i];
}
}
return NULL;
}
/**
* @brief Write data to the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param addr The address to write to.
* @param data The data to be written to the memory location.
* @param len The length of the block to be written.
*/
int wiiuse_write_data(struct wiimote_t* wm, unsigned int addr, const byte* data, byte len) {
byte buf[21] = {0}; /* the payload is always 23 */
byte * bufPtr = buf;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
if (!data || !len) {
return 0;
}
WIIUSE_DEBUG("Writing %i bytes to memory location 0x%x...", len, addr);
#ifdef WITH_WIIUSE_DEBUG
{
int i = 0;
printf("Write data is: ");
for (; i < len; ++i) {
printf("%x ", data[i]);
}
printf("\n");
}
#endif
/* the offset is in big endian */
buffer_big_endian_uint32_t(&bufPtr, (uint32_t)addr);
/* length */
buffer_big_endian_uint8_t(&bufPtr, len);
/* data */
memcpy(bufPtr, data, len);
wiiuse_send(wm, WM_CMD_WRITE_DATA, buf, 21);
return 1;
}
/**
* @brief Write data to the wiimote (callback version).
*
* @param wm Pointer to a wiimote_t structure.
* @param addr The address to write to.
* @param data The data to be written to the memory location.
* @param len The length of the block to be written.
* @param cb Function pointer to call when the data arrives from the wiimote.
*
* The library can only handle one data read request at a time
* because it must keep track of the buffer and other
* events that are specific to that request. So if a request
* has already been made, subsequent requests will be added
* to a pending list and be sent out when the previous
* finishes.
*/
int wiiuse_write_data_cb(struct wiimote_t *wm, unsigned int addr, byte *data, byte len, wiiuse_write_cb write_cb) {
struct data_req_t* req;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return 0;
}
if (!data || !len) {
return 0;
}
req = (struct data_req_t*)malloc(sizeof(struct data_req_t));
req->cb = write_cb;
req->len = len;
memcpy(req->data, data, req->len);
req->state = REQ_READY;
req->addr = addr;/* BIG_ENDIAN_LONG(addr); */
req->next = NULL;
/* add this to the request list */
if (!wm->data_req) {
/* root node */
wm->data_req = req;
WIIUSE_DEBUG("Data write request can be sent out immediately.");
/* send the request out immediately */
wiiuse_send_next_pending_write_request(wm);
} else {
struct data_req_t* nptr = wm->data_req;
WIIUSE_DEBUG("chaud2fois");
for (; nptr->next; nptr = nptr->next) {
;
}
nptr->next = req;
WIIUSE_DEBUG("Added pending data write request.");
}
return 1;
}
/**
* @brief Send the next pending data write request to the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
*
* @see wiiuse_write_data()
*
* This function is not part of the wiiuse API.
*/
void wiiuse_send_next_pending_write_request(struct wiimote_t* wm) {
struct data_req_t* req;
if (!wm || !WIIMOTE_IS_CONNECTED(wm)) {
return;
}
req = wm->data_req;
if (!req) {
return;
}
if (!req->data || !req->len) {
return;
}
if (req->state != REQ_READY) {
return;
}
wiiuse_write_data(wm, req->addr, req->data, req->len);
req->state = REQ_SENT;
return;
}
/**
* @brief Send a packet to the wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param report_type The report type to send (WIIMOTE_CMD_LED, WIIMOTE_CMD_RUMBLE, etc). Found in wiiuse.h
* @param msg The payload. Might be changed by the callee.
* @param len Length of the payload in bytes.
*
* This function should replace any write()s directly to the wiimote device.
*/
int wiiuse_send(struct wiimote_t* wm, byte report_type, byte* msg, int len) {
switch (report_type) {
case WM_CMD_LED:
case WM_CMD_RUMBLE:
case WM_CMD_CTRL_STATUS: {
/* Rumble flag for: 0x11, 0x13, 0x14, 0x15, 0x19 or 0x1a */
if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE)) {
msg[0] |= 0x01;
}
break;
}
default:
break;
}
#ifdef WITH_WIIUSE_DEBUG
{
int x;
printf("[DEBUG] (id %i) SEND: (%.2x) %.2x ", wm->unid, report_type, msg[0]);
for (x = 1; x < len; ++x) {
printf("%.2x ", msg[x]);
}
printf("\n");
}
#endif
return wiiuse_os_write(wm, report_type, msg, len);
}
/**
* @brief Set flags for the specified wiimote.
*
* @param wm Pointer to a wiimote_t structure.
* @param enable Flags to enable.
* @param disable Flags to disable.
*
* @return The flags set after 'enable' and 'disable' have been applied.
*
* The values 'enable' and 'disable' may be any flags OR'ed together.
* Flags are defined in wiiuse.h.
*/
int wiiuse_set_flags(struct wiimote_t* wm, int enable, int disable) {
if (!wm) {
return 0;
}
/* remove mutually exclusive flags */
enable &= ~disable;
disable &= ~enable;
wm->flags |= enable;
wm->flags &= ~disable;
return wm->flags;
}
/**
* @brief Set the wiimote smoothing alpha value.
*
* @param wm Pointer to a wiimote_t structure.
* @param alpha The alpha value to set. Between 0 and 1.
*
* @return Returns the old alpha value.
*
* The alpha value is between 0 and 1 and is used in an exponential
* smoothing algorithm.
*
* Smoothing is only performed if the WIIMOTE_USE_SMOOTHING is set.
*/
float wiiuse_set_smooth_alpha(struct wiimote_t* wm, float alpha) {
float old;
if (!wm) {
return 0.0f;
}
old = wm->accel_calib.st_alpha;
wm->accel_calib.st_alpha = alpha;
/* if there is a nunchuk set that too */
if (wm->exp.type == EXP_NUNCHUK) {
wm->exp.nunchuk.accel_calib.st_alpha = alpha;
}
return old;
}
/**
* @brief Set the bluetooth stack type to use.
*
* @param wm Array of wiimote_t structures.
* @param wiimotes Number of objects in the wm array.
* @param type The type of bluetooth stack to use.
*/
void wiiuse_set_bluetooth_stack(struct wiimote_t** wm, int wiimotes, enum win_bt_stack_t type) {
#ifdef WIIUSE_WIN32
int i;
if (!wm) {
return;
}
for (i = 0; i < wiimotes; ++i) {
wm[i]->stack = type;
}
#endif
}
/**
* @brief Set the orientation event threshold.
*
* @param wm Pointer to a wiimote_t structure.
* @param threshold The decimal place that should be considered a significant change.
*
* If threshold is 0.01, and any angle changes by 0.01 then a significant change
* has occurred and the event callback will be invoked. If threshold is 1 then
* the angle has to change by a full degree to generate an event.
*/
void wiiuse_set_orient_threshold(struct wiimote_t* wm, float threshold) {
if (!wm) {
return;
}
wm->orient_threshold = threshold;
}
/**
* @brief Set the accelerometer event threshold.
*
* @param wm Pointer to a wiimote_t structure.
* @param threshold The decimal place that should be considered a significant change.
*/
void wiiuse_set_accel_threshold(struct wiimote_t* wm, int threshold) {
if (!wm) {
return;
}
wm->accel_threshold = threshold;
}
/**
* @brief Try to resync with the wiimote by starting a new handshake.
*
* @param wm Pointer to a wiimote_t structure.
*/
void wiiuse_resync(struct wiimote_t* wm) {
if (!wm) {
return;
}
#ifndef WIIUSE_SYNC_HANDSHAKE
wm->handshake_state = 0;
#endif
wiiuse_handshake(wm, NULL, 0);
}
/**
* @brief Set the normal and expansion handshake timeouts.
*
* @param wm Array of wiimote_t structures.
* @param wiimotes Number of objects in the wm array.
* @param normal_timeout The timeout in milliseconds for a normal read.
* @param exp_timeout The timeout in millisecondsd to wait for an expansion handshake.
*/
void wiiuse_set_timeout(struct wiimote_t** wm, int wiimotes, byte normal_timeout, byte exp_timeout) {
#ifdef WIIUSE_WIN32
int i;
if (!wm) {
return;
}
for (i = 0; i < wiimotes; ++i) {
wm[i]->normal_timeout = normal_timeout;
wm[i]->exp_timeout = exp_timeout;
}
#endif
}

View File

@@ -0,0 +1,56 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Classic controller expansion device.
*/
#ifndef CLASSIC_H_INCLUDED
#define CLASSIC_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_classic Internal: Classic Controller */
/** @{ */
int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, byte* data, unsigned short len);
void classic_ctrl_disconnected(struct classic_ctrl_t* cc);
void classic_ctrl_event(struct classic_ctrl_t* cc, byte* msg);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* CLASSIC_H_INCLUDED */

View File

@@ -0,0 +1,92 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 General definitions.
*/
#ifndef DEFINITIONS_H_INCLUDED
#define DEFINITIONS_H_INCLUDED
/* this is wiiuse - used to distinguish from third party programs using wiiuse.h */
#include <stdio.h>
#include "definitions_os.h"
/** @addtogroup internal_general */
/** @{ */
#define WIIMOTE_PI 3.14159265f
/* #define WITH_WIIUSE_DEBUG */
extern FILE* logtarget[];
#define OUTF_ERROR logtarget[0]
#define OUTF_WARNING logtarget[1]
#define OUTF_INFO logtarget[2]
#define OUTF_DEBUG logtarget[3]
/* Error output macros */
#define WIIUSE_ERROR(fmt, ...) do { if (OUTF_ERROR) fprintf(OUTF_ERROR, "[ERROR] " fmt "\n", ##__VA_ARGS__); } while(0)
/* Warning output macros */
#define WIIUSE_WARNING(fmt, ...) do { if (OUTF_WARNING) fprintf(OUTF_WARNING, "[WARNING] " fmt "\n", ##__VA_ARGS__); } while(0)
/* Information output macros */
#define WIIUSE_INFO(fmt, ...) do { if (OUTF_INFO) fprintf(OUTF_INFO, "[INFO] " fmt "\n", ##__VA_ARGS__); } while(0)
#ifdef WITH_WIIUSE_DEBUG
#ifdef WIIUSE_WIN32
#define WIIUSE_DEBUG(fmt, ...) do { \
if (OUTF_DEBUG) { \
char* file = __FILE__; \
int i = strlen(file) - 1; \
for (; i && (file[i] != '\\'); --i); \
fprintf(OUTF_DEBUG, "[DEBUG] %s:%i: " fmt "\n", file+i+1, __LINE__, ##__VA_ARGS__); \
} \
} while (0)
#else
#define WIIUSE_DEBUG(fmt, ...) do { if (OUTF_DEBUG) fprintf(OUTF_DEBUG, "[DEBUG] " __FILE__ ":%i: " fmt "\n", __LINE__, ##__VA_ARGS__); } while (0)
#endif
#else
#define WIIUSE_DEBUG(fmt, ...)
#endif
/* Convert between radians and degrees */
#define RAD_TO_DEGREE(r) ((r * 180.0f) / WIIMOTE_PI)
#define DEGREE_TO_RAD(d) (d * (WIIMOTE_PI / 180.0f))
#define absf(x) ((x >= 0) ? (x) : (x * -1.0f))
#define diff_f(x, y) ((x >= y) ? (absf(x - y)) : (absf(y - x)))
#define WCONST
/** @} */
#endif /* DEFINITIONS_H_INCLUDED */

View File

@@ -0,0 +1,51 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Operating system related definitions.
*
* This file is an attempt to separate operating system
* dependent functions and choose what should be used
* at compile time.
*/
#ifndef DEFINITIONS_OS_H_INCLUDED
#define DEFINITIONS_OS_H_INCLUDED
#ifdef _MSC_VER
#include <float.h>
/* windows with visual c */
#define isnan(x) (_isnan(x))
#define isinf(x) (!_finite(x))
/* disable warnings I don't care about */
/*#pragma warning(disable:4273) */ /* inconsistent dll linkage */
#endif
#endif /* DEFINITIONS_OS_H_INCLUDED */

View File

@@ -0,0 +1,60 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 the dynamics of the wiimote.
*
* The file includes functions that handle the dynamics
* of the wiimote. Such dynamics include orientation and
* motion sensing.
*/
#ifndef DYNAMICS_H_INCLUDED
#define DYNAMICS_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_dynamics Internal: Dynamics Functions */
/** @{ */
void calculate_orientation(struct accel_t* ac, struct vec3b_t* accel, struct orient_t* orient, int smooth);
void calculate_gforce(struct accel_t* ac, struct vec3b_t* accel, struct gforce_t* gforce);
void calc_joystick_state(struct joystick_t* js, float x, float y);
void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* DYNAMICS_H_INCLUDED */

64
src/main/headers/events.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 wiimote events.
*
* The file includes functions that handle the events
* that are sent from the wiimote to us.
*/
#ifndef EVENTS_H_INCLUDED
#define EVENTS_H_INCLUDED
#if defined(_MSC_VER)
/* MS compilers of pre-VC2010 versions don't have stdint.h
* and I can't get VC2010's stdint.h to compile nicely in
* WiiUse
*/
#include "wiiuse_msvcstdint.h"
#else
#include <stdint.h>
#endif
/** @defgroup internal_events Internal: Event Utilities */
/** @{ */
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);
void idle_cycle(struct wiimote_t* wm);
void clear_dirty_reads(struct wiimote_t* wm);
/** @} */
#endif /* EVENTS_H_INCLUDED */

View File

@@ -0,0 +1,66 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Guitar Hero 3 expansion device.
*/
#ifndef GUITAR_HERO_3_H_INCLUDED
#define GUITAR_HERO_3_H_INCLUDED
#include "wiiuse_internal.h"
#define GUITAR_HERO_3_JS_MIN_X 0xC5
#define GUITAR_HERO_3_JS_MAX_X 0xFC
#define GUITAR_HERO_3_JS_CENTER_X 0xE0
#define GUITAR_HERO_3_JS_MIN_Y 0xC5
#define GUITAR_HERO_3_JS_MAX_Y 0xFA
#define GUITAR_HERO_3_JS_CENTER_Y 0xE0
#define GUITAR_HERO_3_WHAMMY_BAR_MIN 0xEF
#define GUITAR_HERO_3_WHAMMY_BAR_MAX 0xFA
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_gh3 Internal: Guitar Hero 3 controller */
/** @{ */
int guitar_hero_3_handshake(struct wiimote_t* wm, struct guitar_hero_3_t* gh3, byte* data, unsigned short len);
void guitar_hero_3_disconnected(struct guitar_hero_3_t* gh3);
void guitar_hero_3_event(struct guitar_hero_3_t* gh3, byte* msg);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* GUITAR_HERO_3_H_INCLUDED */

55
src/main/headers/io.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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.
*/
#ifndef IO_H_INCLUDED
#define IO_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_io Internal: Device I/O */
/** @{ */
void wiiuse_handshake(struct wiimote_t* wm, byte* data, uint16_t len);
void wiiuse_wait_report(struct wiimote_t *wm, int report, byte *buffer, int bufferLength);
void wiiuse_read_data_sync(struct wiimote_t *wm, byte memory, unsigned addr, unsigned short size, byte *data);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* IO_H_INCLUDED */

61
src/main/headers/ir.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 IR data.
*/
#ifndef IR_H_INCLUDED
#define IR_H_INCLUDED
#include "wiiuse_internal.h"
#define WII_VRES_X 560
#define WII_VRES_Y 340
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_ir Internal: IR Sensor */
/** @{ */
void wiiuse_set_ir_mode(struct wiimote_t *wm);
void calculate_basic_ir(struct wiimote_t* wm, byte* data);
void calculate_extended_ir(struct wiimote_t* wm, byte* data);
float calc_yaw(struct ir_t* ir);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* IR_H_INCLUDED */

View File

@@ -0,0 +1,60 @@
/*
* wiiuse
*
* Written By:
* Michal Wiedenbauer < shagkur >
* Dave Murphy < WinterMute >
* Hector Martin < marcan >
* Radu Andries <admiral0>
*
* Copyright 2009
*
* This file is part of wiiuse and fWIIne.
*
* 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 Motion plus extension
*/
#ifndef MOTION_PLUS_H_INCLUDED
#define MOTION_PLUS_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_mp Internal: MotionPlus */
/** @{ */
void motion_plus_disconnected(struct motion_plus_t* mp);
void motion_plus_event(struct motion_plus_t* mp, int exp_type, byte* msg);
void wiiuse_motion_plus_handshake(struct wiimote_t *wm, byte *data, unsigned short len);
void wiiuse_probe_motion_plus(struct wiimote_t *wm);
/** @} */
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,59 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Nunchuk expansion device.
*/
#ifndef NUNCHUK_H_INCLUDED
#define NUNCHUK_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_nunchuk Internal: Nunchuk */
/** @{ */
int nunchuk_handshake(struct wiimote_t* wm, struct nunchuk_t* nc, byte* data, unsigned short len);
void nunchuk_disconnected(struct nunchuk_t* nc);
void nunchuk_event(struct nunchuk_t* nc, byte* msg);
void nunchuk_pressed_buttons(struct nunchuk_t* nc, byte now);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* NUNCHUK_H_INCLUDED */

64
src/main/headers/os.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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.
*/
#ifndef PLATFORM_H_INCLUDED
#define PLATFORM_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_io Internal: Platform-specific Device I/O */
/** @{ */
void wiiuse_init_platform_fields(struct wiimote_t* wm);
void wiiuse_cleanup_platform_fields(struct wiimote_t* wm);
int wiiuse_os_find(struct wiimote_t** wm, int max_wiimotes, int timeout);
int wiiuse_os_connect(struct wiimote_t** wm, int wiimotes);
void wiiuse_os_disconnect(struct wiimote_t* wm);
int wiiuse_os_poll(struct wiimote_t** wm, int wiimotes);
/* buf[0] will be the report type, buf+1 the rest of the report */
int wiiuse_os_read(struct wiimote_t* wm, byte* buf, int len);
int wiiuse_os_write(struct wiimote_t* wm, byte report_type, byte* buf, int len);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* PLATFORM_H_INCLUDED */

View File

@@ -0,0 +1,55 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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 Wii balance board expansion device.
*/
#ifndef WII_BOARD_H_INCLUDED
#define WII_BOARD_H_INCLUDED
#include "wiiuse_internal.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup internal_wiiboard Internal: Wii Balance Board */
/** @{ */
int wii_board_handshake(struct wiimote_t* wm, struct wii_board_t* wb, byte* data, uint16_t len);
void wii_board_disconnected(struct wii_board_t* wb);
void wii_board_event(struct wii_board_t* wb, byte* msg);
/** @} */
#ifdef __cplusplus
}
#endif
#endif

956
src/main/headers/wiiuse.h Normal file
View File

@@ -0,0 +1,956 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* 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
* 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 API header file.
*
* If this file is included from inside the wiiuse source
* and not from a third party program, then wiimote_internal.h
* is also included which extends this file.
*/
/**
* @mainpage
*
* @section intro Introduction
*
* WiiUse is a cross-platform C library for accessing the Nintendo Wii
* Remote and its related expansions and variations.
*
* @section project Project
*
* This is a friendly fork of the original WiiUse project, which seems
* to have gone defunct. This updated version incorporates improvements
* from a number of internal forks found across the Internet, and is
* intended to be the new "upstream" of the project. The new homepage is
* on GitHub, where the source is maintained:
*
* - http://github.com/rpavlik/wiiuse
*
* Contributions (under the GPL 3+) are welcome and encouraged!
*
* @section publicapisec Public API
*
* - @ref publicapi "Public API" - entirely within @ref wiiuse.h
* - @ref wiimote "Wiimote device structure"
*
*/
#ifndef WIIUSE_H_INCLUDED
#define WIIUSE_H_INCLUDED
#define WIIUSE_MAJOR 0
#define WIIUSE_MINOR 14
#define WIIUSE_MICRO 2
#define WIIUSE_VERSION_TRANSFORM(MAJ, MIN, MICRO) (MAJ * 1000000 + MIN * 1000 + MICRO)
#define WIIUSE_HAS_VERSION(MAJ, MIN, MICRO) ( WIIUSE_VERSION_TRANSFORM(MAJ, MIN, MICRO) <= WIIUSE_VERSION_TRANSFORM(WIIUSE_MAJOR, WIIUSE_MINOR, WIIUSE_MICRO) )
#ifndef WIIUSE_PLATFORM
#if defined(_WIN32)
#define WIIUSE_PLATFORM
#define WIIUSE_WIN32
#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
#endif
#ifdef WIIUSE_WIN32
/* windows */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#endif
#ifdef WIIUSE_BLUEZ
/* nix */
#include <bluetooth/bluetooth.h>
#endif
#ifndef WCONST
#define WCONST const
#endif
#if defined(_MSC_VER)
/* MS compilers of pre-VC2010 versions don't have stdint.h
* and I can't get VC2010's stdint.h to compile nicely in
* WiiUse
*/
#include "wiiuse_msvcstdint.h"
#else
#include <stdint.h>
#endif
#include <stdio.h> /* for FILE */
/** @defgroup publicapi External API */
/** @{ */
/** @name Wiimote state flags and macros */
/** @{ */
#define WIIMOTE_STATE_DEV_FOUND 0x0001
#define WIIMOTE_STATE_HANDSHAKE 0x0002 /* actual connection exists but no handshake yet */
#define WIIMOTE_STATE_HANDSHAKE_COMPLETE 0x0004 /* actual connection exists but no handshake yet */
#define WIIMOTE_STATE_CONNECTED 0x0008
#define WIIMOTE_STATE_RUMBLE 0x0010
#define WIIMOTE_STATE_ACC 0x0020
#define WIIMOTE_STATE_EXP 0x0040
#define WIIMOTE_STATE_IR 0x0080
#define WIIMOTE_STATE_SPEAKER 0x0100
#define WIIMOTE_STATE_IR_SENS_LVL1 0x0200
#define WIIMOTE_STATE_IR_SENS_LVL2 0x0400
#define WIIMOTE_STATE_IR_SENS_LVL3 0x0800
#define WIIMOTE_STATE_IR_SENS_LVL4 0x1000
#define WIIMOTE_STATE_IR_SENS_LVL5 0x2000
#define WIIMOTE_STATE_EXP_HANDSHAKE 0x10000 /* actual M+ connection exists but no handshake yet */
#define WIIMOTE_STATE_EXP_EXTERN 0x20000 /* actual M+ connection exists but handshake failed */
#define WIIMOTE_STATE_EXP_FAILED 0x40000 /* actual M+ connection exists but handshake failed */
#define WIIMOTE_STATE_MPLUS_PRESENT 0x80000 /* Motion+ is connected */
#define WIIMOTE_ID(wm) (wm->unid)
#define WIIMOTE_IS_SET(wm, s) ((wm->state & (s)) == (s))
#define WIIMOTE_IS_CONNECTED(wm) (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_CONNECTED))
/** @} */
/** @name LED bit masks */
/** @{ */
#define WIIMOTE_LED_NONE 0x00
#define WIIMOTE_LED_1 0x10
#define WIIMOTE_LED_2 0x20
#define WIIMOTE_LED_3 0x40
#define WIIMOTE_LED_4 0x80
/** @} */
/** @name Button codes */
/** @{ */
#define WIIMOTE_BUTTON_TWO 0x0001
#define WIIMOTE_BUTTON_ONE 0x0002
#define WIIMOTE_BUTTON_B 0x0004
#define WIIMOTE_BUTTON_A 0x0008
#define WIIMOTE_BUTTON_MINUS 0x0010
#define WIIMOTE_BUTTON_ZACCEL_BIT6 0x0020
#define WIIMOTE_BUTTON_ZACCEL_BIT7 0x0040
#define WIIMOTE_BUTTON_HOME 0x0080
#define WIIMOTE_BUTTON_LEFT 0x0100
#define WIIMOTE_BUTTON_RIGHT 0x0200
#define WIIMOTE_BUTTON_DOWN 0x0400
#define WIIMOTE_BUTTON_UP 0x0800
#define WIIMOTE_BUTTON_PLUS 0x1000
#define WIIMOTE_BUTTON_ZACCEL_BIT4 0x2000
#define WIIMOTE_BUTTON_ZACCEL_BIT5 0x4000
#define WIIMOTE_BUTTON_UNKNOWN 0x8000
#define WIIMOTE_BUTTON_ALL 0x1F9F
/** @} */
/** @name Nunchuk button codes */
/** @{ */
#define NUNCHUK_BUTTON_Z 0x01
#define NUNCHUK_BUTTON_C 0x02
#define NUNCHUK_BUTTON_ALL 0x03
/** @} */
/** @name Classic controller button codes */
/** @{ */
#define CLASSIC_CTRL_BUTTON_UP 0x0001
#define CLASSIC_CTRL_BUTTON_LEFT 0x0002
#define CLASSIC_CTRL_BUTTON_ZR 0x0004
#define CLASSIC_CTRL_BUTTON_X 0x0008
#define CLASSIC_CTRL_BUTTON_A 0x0010
#define CLASSIC_CTRL_BUTTON_Y 0x0020
#define CLASSIC_CTRL_BUTTON_B 0x0040
#define CLASSIC_CTRL_BUTTON_ZL 0x0080
#define CLASSIC_CTRL_BUTTON_FULL_R 0x0200
#define CLASSIC_CTRL_BUTTON_PLUS 0x0400
#define CLASSIC_CTRL_BUTTON_HOME 0x0800
#define CLASSIC_CTRL_BUTTON_MINUS 0x1000
#define CLASSIC_CTRL_BUTTON_FULL_L 0x2000
#define CLASSIC_CTRL_BUTTON_DOWN 0x4000
#define CLASSIC_CTRL_BUTTON_RIGHT 0x8000
#define CLASSIC_CTRL_BUTTON_ALL 0xFEFF
/** @} */
/** @name Guitar Hero 3 button codes */
/** @{ */
#define GUITAR_HERO_3_BUTTON_STRUM_UP 0x0001
#define GUITAR_HERO_3_BUTTON_YELLOW 0x0008
#define GUITAR_HERO_3_BUTTON_GREEN 0x0010
#define GUITAR_HERO_3_BUTTON_BLUE 0x0020
#define GUITAR_HERO_3_BUTTON_RED 0x0040
#define GUITAR_HERO_3_BUTTON_ORANGE 0x0080
#define GUITAR_HERO_3_BUTTON_PLUS 0x0400
#define GUITAR_HERO_3_BUTTON_MINUS 0x1000
#define GUITAR_HERO_3_BUTTON_STRUM_DOWN 0x4000
#define GUITAR_HERO_3_BUTTON_ALL 0xFEFF
/** @} */
/** @name Wiimote option flags */
/** @{ */
#define WIIUSE_SMOOTHING 0x01
#define WIIUSE_CONTINUOUS 0x02
#define WIIUSE_ORIENT_THRESH 0x04
#define WIIUSE_INIT_FLAGS (WIIUSE_SMOOTHING | WIIUSE_ORIENT_THRESH)
#define WIIUSE_ORIENT_PRECISION 100.0f
/** @} */
/** @name Expansion codes */
/** @{ */
#define EXP_NONE 0
#define EXP_NUNCHUK 1
#define EXP_CLASSIC 2
#define EXP_GUITAR_HERO_3 3
#define EXP_WII_BOARD 4
#define EXP_MOTION_PLUS 5
#define EXP_MOTION_PLUS_NUNCHUK 6 /* Motion+ in nunchuk pass-through mode */
#define EXP_MOTION_PLUS_CLASSIC 7 /* Motion+ in classic ctr. pass-through mode */
/** @} */
/** @brief IR correction types */
typedef enum ir_position_t {
WIIUSE_IR_ABOVE,
WIIUSE_IR_BELOW
} ir_position_t;
/** @name Device Inquiry Macros */
/** @{ */
/**
* @brief Check if a button is pressed.
* @param dev Pointer to a wiimote_t or expansion structure.
* @param button The button you are interested in.
* @return 1 if the button is pressed, 0 if not.
*/
#define IS_PRESSED(dev, button) ((dev->btns & button) == button)
/**
* @brief Check if a button is being held.
* @param dev Pointer to a wiimote_t or expansion structure.
* @param button The button you are interested in.
* @return 1 if the button is held, 0 if not.
*/
#define IS_HELD(dev, button) ((dev->btns_held & button) == button)
/**
* @brief Check if a button is released on this event. \n\n
* This does not mean the button is not pressed, it means \n
* this button was just now released.
* @param dev Pointer to a wiimote_t or expansion structure.
* @param button The button you are interested in.
* @return 1 if the button is released, 0 if not.
*
*/
#define IS_RELEASED(dev, button) ((dev->btns_released & button) == button)
/**
* @brief Check if a button has just been pressed this event.
* @param dev Pointer to a wiimote_t or expansion structure.
* @param button The button you are interested in.
* @return 1 if the button is pressed, 0 if not.
*/
#define IS_JUST_PRESSED(dev, button) (IS_PRESSED(dev, button) && !IS_HELD(dev, button))
/**
* @brief Return the IR sensitivity level.
* @param wm Pointer to a wiimote_t structure.
* @param lvl [out] Pointer to an int that will hold the level setting.
* If no level is set 'lvl' will be set to 0.
*/
#define WIIUSE_GET_IR_SENSITIVITY(wm, lvl) \
do { \
if ((wm->state & 0x0200) == 0x0200) *lvl = 1; \
else if ((wm->state & 0x0400) == 0x0400) *lvl = 2; \
else if ((wm->state & 0x0800) == 0x0800) *lvl = 3; \
else if ((wm->state & 0x1000) == 0x1000) *lvl = 4; \
else if ((wm->state & 0x2000) == 0x2000) *lvl = 5; \
else *lvl = 0; \
} while (0)
#define WIIUSE_USING_ACC(wm) ((wm->state & 0x020) == 0x020)
#define WIIUSE_USING_EXP(wm) ((wm->state & 0x040) == 0x040)
#define WIIUSE_USING_IR(wm) ((wm->state & 0x080) == 0x080)
#define WIIUSE_USING_SPEAKER(wm) ((wm->state & 0x100) == 0x100)
#define WIIUSE_IS_LED_SET(wm, num) ((wm->leds & WIIMOTE_LED_##num) == WIIMOTE_LED_##num)
/** @} */
/*
* This is left over from an old hack, but it may actually
* be a useful feature to keep so it wasn't removed.
*/
#ifdef WIIUSE_WIN32
#define WIIMOTE_DEFAULT_TIMEOUT 10
#define WIIMOTE_EXP_TIMEOUT 10
#endif
#define WIIUSE_SYNC_HANDSHAKE
typedef unsigned char byte;
typedef char sbyte;
struct wiimote_t;
struct vec3b_t;
struct orient_t;
struct gforce_t;
/**
* @brief Callback that handles a read event.
*
* @param wm Pointer to a wiimote_t structure.
* @param data Pointer to the filled data block.
* @param len Length in bytes of the data block.
*
* @see wiiuse_init()
*
* A registered function of this type is called automatically by the wiiuse
* library when the wiimote has returned the full data requested by a previous
* call to wiiuse_read_data().
*/
typedef void (*wiiuse_read_cb)(struct wiimote_t* wm, byte* data, uint16_t len);
/**
* @brief Data read request structure.
*/
struct read_req_t {
wiiuse_read_cb cb; /**< read data callback */
byte* buf; /**< buffer where read data is written */
uint32_t addr; /**< the offset that the read started at */
uint16_t size; /**< the length of the data read */
uint16_t wait; /**< num bytes still needed to finish read */
byte dirty; /**< set to 1 if not using callback and needs to be cleaned up */
struct read_req_t* next; /**< next read request in the queue */
};
/**
* @struct ang3s_t
* @brief Roll/Pitch/Yaw short angles.
*/
typedef struct ang3s_t {
int16_t roll, pitch, yaw;
} ang3s_t;
/**
* @struct ang3f_t
* @brief Roll/Pitch/Yaw float angles.
*/
typedef struct ang3f_t {
float roll, pitch, yaw;
} ang3f_t;
/**
* @brief Unsigned x,y byte vector.
*/
typedef struct vec2b_t {
byte x, y;
} vec2b_t;
/**
* @brief Unsigned x,y,z byte vector.
*/
typedef struct vec3b_t {
byte x, y, z;
} vec3b_t;
/**
* @brief Signed x,y,z float struct.
*/
typedef struct vec3f_t {
float x, y, z;
} vec3f_t;
/**
* @brief Orientation struct.
*
* Yaw, pitch, and roll range from -180 to 180 degrees.
*/
typedef struct orient_t {
float roll; /**< roll, this may be smoothed if enabled */
float pitch; /**< pitch, this may be smoothed if enabled */
float yaw;
float a_roll; /**< absolute roll, unsmoothed */
float a_pitch; /**< absolute pitch, unsmoothed */
} orient_t;
/**
* @brief Gravity force struct.
*/
typedef struct gforce_t {
float x, y, z;
} gforce_t;
/**
* @brief Accelerometer struct. For any device with an accelerometer.
*/
typedef struct accel_t {
struct vec3b_t cal_zero; /**< zero calibration */
struct vec3b_t cal_g; /**< 1g difference around 0cal */
float st_roll; /**< last smoothed roll value */
float st_pitch; /**< last smoothed roll pitch */
float st_alpha; /**< alpha value for smoothing [0-1] */
} accel_t;
/**
* @brief A single IR source.
*/
typedef struct ir_dot_t {
byte visible; /**< if the IR source is visible */
unsigned int x; /**< interpolated X coordinate */
unsigned int y; /**< interpolated Y coordinate */
int16_t rx; /**< raw X coordinate (0-1023) */
int16_t ry; /**< raw Y coordinate (0-767) */
byte order; /**< increasing order by x-axis value */
byte size; /**< size of the IR dot (0-15) */
} ir_dot_t;
/**
* @brief Screen aspect ratio.
*/
typedef enum aspect_t {
WIIUSE_ASPECT_4_3,
WIIUSE_ASPECT_16_9
} aspect_t;
/**
* @brief IR struct. Hold all data related to the IR tracking.
*/
typedef struct ir_t {
struct ir_dot_t dot[4]; /**< IR dots */
byte num_dots; /**< number of dots at this time */
enum aspect_t aspect; /**< aspect ratio of the screen */
enum ir_position_t pos; /**< IR sensor bar position */
unsigned int vres[2]; /**< IR virtual screen resolution */
int offset[2]; /**< IR XY correction offset */
int state; /**< keeps track of the IR state */
int ax; /**< absolute X coordinate */
int ay; /**< absolute Y coordinate */
int x; /**< calculated X coordinate */
int y; /**< calculated Y coordinate */
float distance; /**< pixel distance between first 2 dots*/
float z; /**< calculated distance */
} ir_t;
/**
* @brief Joystick calibration structure.
*
* The angle \a ang is relative to the positive y-axis into quadrant I
* and ranges from 0 to 360 degrees. So if the joystick is held straight
* upwards then angle is 0 degrees. If it is held to the right it is 90,
* down is 180, and left is 270.
*
* The magnitude \a mag is the distance from the center to where the
* joystick is being held. The magnitude ranges from 0 to 1.
* If the joystick is only slightly tilted from the center the magnitude
* will be low, but if it is closer to the outter edge the value will
* be higher.
*/
typedef struct joystick_t {
struct vec2b_t max; /**< maximum joystick values */
struct vec2b_t min; /**< minimum joystick values */
struct vec2b_t center; /**< center joystick values */
float ang; /**< angle the joystick is being held */
float mag; /**< magnitude of the joystick (range 0-1) */
float x; /**< horizontal position of the joystick (range [-1, 1] */
float y; /**< vertical position of the joystick (range [-1, 1] */
} joystick_t;
/**
* @brief Nunchuk expansion device.
*/
typedef struct nunchuk_t {
struct accel_t accel_calib; /**< nunchuk accelerometer calibration */
struct joystick_t js; /**< joystick calibration */
int* flags; /**< options flag (points to wiimote_t.flags) */
byte btns; /**< what buttons have just been pressed */
byte btns_held; /**< what buttons are being held down */
byte btns_released; /**< what buttons were just released this */
float orient_threshold; /**< threshold for orient to generate an event */
int accel_threshold; /**< threshold for accel to generate an event */
struct vec3b_t accel; /**< current raw acceleration data */
struct orient_t orient; /**< current orientation on each axis */
struct gforce_t gforce; /**< current gravity forces on each axis */
} nunchuk_t;
/**
* @brief Classic controller expansion device.
*/
typedef struct classic_ctrl_t {
int16_t btns; /**< what buttons have just been pressed */
int16_t btns_held; /**< what buttons are being held down */
int16_t btns_released; /**< what buttons were just released this */
float r_shoulder; /**< right shoulder button (range 0-1) */
float l_shoulder; /**< left shoulder button (range 0-1) */
struct joystick_t ljs; /**< left joystick calibration */
struct joystick_t rjs; /**< right joystick calibration */
} classic_ctrl_t;
/**
* @brief Guitar Hero 3 expansion device.
*/
typedef struct guitar_hero_3_t {
int16_t btns; /**< what buttons have just been pressed */
int16_t btns_held; /**< what buttons are being held down */
int16_t btns_released; /**< what buttons were just released this */
float whammy_bar; /**< whammy bar (range 0-1) */
struct joystick_t js; /**< joystick calibration */
} guitar_hero_3_t;
/**
* @brief Motion Plus expansion device
*/
typedef struct motion_plus_t {
byte ext; /**< is there a device on the pass-through port? */
struct ang3s_t raw_gyro; /**< current raw gyroscope data */
struct ang3s_t cal_gyro; /**< calibration raw gyroscope data */
struct ang3f_t angle_rate_gyro; /**< current gyro angle rate */
struct orient_t orient; /**< current orientation on each axis using Motion Plus gyroscopes */
byte acc_mode; /**< Fast/slow rotation mode for roll, pitch and yaw (0 if rotating fast, 1 if slow or still) */
int raw_gyro_threshold; /**< threshold for gyroscopes to generate an event */
struct nunchuk_t *nc; /**< pointers to nunchuk & classic in pass-through-mode */
struct classic_ctrl_t *classic;
} motion_plus_t;
/**
* @brief Wii Balance Board "expansion" device.
*
* A Balance Board presents itself as a Wiimote with a permanently-attached
* Balance Board expansion device.
*/
typedef struct wii_board_t {
/** @name Interpolated weight per sensor (kg)
*
* These are the values you're most likely to use.
*
* See example.c for how to compute total weight and center of gravity
* from these values.
*/
/** @{ */
float tl;
float tr;
float bl;
float br;
/** @} */
/** @name Raw sensor values */
/** @{ */
uint16_t rtl;
uint16_t rtr;
uint16_t rbl;
uint16_t rbr;
/** @} */
/** @name Sensor calibration values */
/** @{ */
uint16_t ctl[3]; /* Calibration */
uint16_t ctr[3];
uint16_t cbl[3];
uint16_t cbr[3]; /* /Calibration */
/** @} */
uint8_t update_calib;
} wii_board_t;
/**
* @brief Generic expansion device plugged into wiimote.
*/
typedef struct expansion_t {
int type; /**< type of expansion attached */
struct motion_plus_t mp;
union {
struct nunchuk_t nunchuk;
struct classic_ctrl_t classic;
struct guitar_hero_3_t gh3;
struct wii_board_t wb;
};
} expansion_t;
/**
* @brief Available bluetooth stacks for Windows.
*/
typedef enum win_bt_stack_t {
WIIUSE_STACK_UNKNOWN,
WIIUSE_STACK_MS,
WIIUSE_STACK_BLUESOLEIL
} win_bt_stack_t;
/**
* @brief Significant data from the previous event.
*/
typedef struct wiimote_state_t {
/* expansion_t */
float exp_ljs_ang;
float exp_rjs_ang;
float exp_ljs_mag;
float exp_rjs_mag;
uint16_t exp_btns;
struct orient_t exp_orient;
struct vec3b_t exp_accel;
float exp_r_shoulder;
float exp_l_shoulder;
/* motion plus */
short drx;
short dry;
short drz;
/* wiiboard */
uint16_t exp_wb_rtr;
uint16_t exp_wb_rtl;
uint16_t exp_wb_rbr;
uint16_t exp_wb_rbl;
/* ir_t */
int ir_ax;
int ir_ay;
float ir_distance;
struct orient_t orient;
uint16_t btns;
struct vec3b_t accel;
} wiimote_state_t;
/**
* @brief Events that wiiuse can generate from a poll.
*/
typedef enum WIIUSE_EVENT_TYPE {
WIIUSE_NONE = 0,
WIIUSE_EVENT,
WIIUSE_STATUS,
WIIUSE_CONNECT,
WIIUSE_DISCONNECT,
WIIUSE_UNEXPECTED_DISCONNECT,
WIIUSE_READ_DATA,
WIIUSE_WRITE_DATA,
WIIUSE_NUNCHUK_INSERTED,
WIIUSE_NUNCHUK_REMOVED,
WIIUSE_CLASSIC_CTRL_INSERTED,
WIIUSE_CLASSIC_CTRL_REMOVED,
WIIUSE_GUITAR_HERO_3_CTRL_INSERTED,
WIIUSE_GUITAR_HERO_3_CTRL_REMOVED,
WIIUSE_WII_BOARD_CTRL_INSERTED,
WIIUSE_WII_BOARD_CTRL_REMOVED,
WIIUSE_MOTION_PLUS_ACTIVATED,
WIIUSE_MOTION_PLUS_REMOVED
} WIIUSE_EVENT_TYPE;
/**
* @brief Main Wiimote device structure.
*
* You need one of these to do pretty much anything with this library.
*/
typedef struct wiimote_t {
WCONST int unid; /**< user specified id */
#ifdef WIIUSE_BLUEZ
/** @name Linux-specific (BlueZ) members */
/** @{ */
WCONST char bdaddr_str[18]; /**< readable bt address */
WCONST bdaddr_t bdaddr; /**< bt address */
WCONST int out_sock; /**< output socket */
WCONST int in_sock; /**< input socket */
/** @} */
#endif
#ifdef WIIUSE_WIN32
/** @name Windows-specific members */
/** @{ */
WCONST HANDLE dev_handle; /**< HID handle */
WCONST OVERLAPPED hid_overlap; /**< overlap handle */
WCONST enum win_bt_stack_t stack; /**< type of bluetooth stack to use */
WCONST int timeout; /**< read timeout */
WCONST byte normal_timeout; /**< normal timeout */
WCONST byte exp_timeout; /**< timeout for expansion handshake */
/** @} */
#endif
#ifdef WIIUSE_MAC
/** @name Mac OS X-specific members */
/** @{ */
WCONST void* objc_wm; /** WiiuseWiimote* as opaque pointer */
/** @} */
#endif
WCONST int state; /**< various state flags */
WCONST byte leds; /**< currently lit leds */
WCONST float battery_level; /**< battery level */
WCONST int flags; /**< options flag */
#ifndef WIIUSE_SYNC_HANDSHAKE
WCONST byte handshake_state; /**< the state of the connection handshake */
#endif
WCONST byte expansion_state; /**< the state of the expansion handshake */
WCONST struct data_req_t* data_req; /**< list of data read requests */
WCONST struct read_req_t* read_req; /**< list of data read requests */
WCONST struct accel_t accel_calib; /**< wiimote accelerometer calibration */
WCONST struct expansion_t exp; /**< wiimote expansion device */
WCONST struct vec3b_t accel; /**< current raw acceleration data */
WCONST struct orient_t orient; /**< current orientation on each axis */
WCONST struct gforce_t gforce; /**< current gravity forces on each axis */
WCONST struct ir_t ir; /**< IR data */
WCONST uint16_t btns; /**< what buttons have just been pressed */
WCONST uint16_t btns_held; /**< what buttons are being held down */
WCONST uint16_t btns_released; /**< what buttons were just released this */
WCONST float orient_threshold; /**< threshold for orient to generate an event */
WCONST int32_t accel_threshold; /**< threshold for accel to generate an event */
WCONST struct wiimote_state_t lstate; /**< last saved state */
WCONST WIIUSE_EVENT_TYPE event; /**< type of event that occurred */
WCONST byte motion_plus_id[6];
} wiimote;
/** @brief Data passed to a callback during wiiuse_update() */
typedef struct wiimote_callback_data_t {
WCONST int uid;
WCONST byte leds;
WCONST float battery_level;
WCONST struct vec3b_t accel;
WCONST struct orient_t orient;
WCONST struct gforce_t gforce;
WCONST struct ir_t ir;
WCONST uint16_t buttons;
WCONST uint16_t buttons_held;
WCONST uint16_t buttons_released;
WCONST WIIUSE_EVENT_TYPE event;
WCONST int state;
WCONST struct expansion_t expansion;
} wiimote_callback_data_t;
/** @brief Callback type */
typedef void (*wiiuse_update_cb)(struct wiimote_callback_data_t* wm);
/**
* @brief Callback that handles a write event.
*
* @param wm Pointer to a wiimote_t structure.
* @param data Pointer to the sent data block.
* @param len Length in bytes of the data block.
*
* @see wiiuse_init()
*
* A registered function of this type is called automatically by the wiiuse
* library when the wiimote has returned the full data requested by a previous
* call to wiiuse_write_data().
*/
typedef void (*wiiuse_write_cb)(struct wiimote_t* wm, unsigned char* data, unsigned short len);
typedef enum data_req_s {
REQ_READY = 0,
REQ_SENT,
REQ_DONE
} data_req_s;
/**
* @struct data_req_t
* @brief Data write request structure.
*/
struct data_req_t {
byte data[21]; /**< buffer where read data is written */
byte len;
unsigned int addr;
data_req_s state; /**< set to 1 if not using callback and needs to be cleaned up */
wiiuse_write_cb cb; /**< read data callback */
struct data_req_t *next;
};
/**
* @brief Loglevels supported by wiiuse.
*/
typedef enum wiiuse_loglevel {
LOGLEVEL_ERROR = 0,
LOGLEVEL_WARNING = 1,
LOGLEVEL_INFO = 2,
LOGLEVEL_DEBUG = 3
} wiiuse_loglevel;
/*****************************************
*
* Include API specific stuff
*
*****************************************/
#ifdef WIIUSE_WIN32
#ifdef WIIUSE_STATIC
#define WIIUSE_EXPORT_DECL
#define WIIUSE_IMPORT_DECL
#else
#define WIIUSE_EXPORT_DECL __declspec(dllexport)
#define WIIUSE_IMPORT_DECL __declspec(dllimport)
#endif
#else
#define WIIUSE_EXPORT_DECL
#define WIIUSE_IMPORT_DECL
#endif
#ifdef WIIUSE_COMPILE_LIB
#define WIIUSE_EXPORT WIIUSE_EXPORT_DECL
#else
#define WIIUSE_EXPORT WIIUSE_IMPORT_DECL
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* wiiuse.c */
WIIUSE_EXPORT extern const char* wiiuse_version();
/** @brief Define indicating the presence of the feature allowing you to
* redirect output for one or more logging levels within the library.
*/
#define WIIUSE_HAS_OUTPUT_REDIRECTION
WIIUSE_EXPORT extern void wiiuse_set_output(enum wiiuse_loglevel loglevel, FILE *logtarget);
WIIUSE_EXPORT extern struct wiimote_t** wiiuse_init(int wiimotes);
WIIUSE_EXPORT extern void wiiuse_disconnected(struct wiimote_t* wm);
WIIUSE_EXPORT extern void wiiuse_cleanup(struct wiimote_t** wm, int wiimotes);
WIIUSE_EXPORT extern void wiiuse_rumble(struct wiimote_t* wm, int status);
WIIUSE_EXPORT extern void wiiuse_toggle_rumble(struct wiimote_t* wm);
WIIUSE_EXPORT extern void wiiuse_set_leds(struct wiimote_t* wm, int leds);
WIIUSE_EXPORT extern void wiiuse_motion_sensing(struct wiimote_t* wm, int status);
WIIUSE_EXPORT extern int wiiuse_read_data(struct wiimote_t* wm, byte* buffer, unsigned int offset, uint16_t len);
WIIUSE_EXPORT extern int wiiuse_write_data(struct wiimote_t* wm, unsigned int addr, const byte* data, byte len);
WIIUSE_EXPORT extern void wiiuse_status(struct wiimote_t* wm);
WIIUSE_EXPORT extern struct wiimote_t* wiiuse_get_by_id(struct wiimote_t** wm, int wiimotes, int unid);
WIIUSE_EXPORT extern int wiiuse_set_flags(struct wiimote_t* wm, int enable, int disable);
WIIUSE_EXPORT extern float wiiuse_set_smooth_alpha(struct wiimote_t* wm, float alpha);
WIIUSE_EXPORT extern void wiiuse_set_bluetooth_stack(struct wiimote_t** wm, int wiimotes, enum win_bt_stack_t type);
WIIUSE_EXPORT extern void wiiuse_set_orient_threshold(struct wiimote_t* wm, float threshold);
WIIUSE_EXPORT extern void wiiuse_resync(struct wiimote_t* wm);
WIIUSE_EXPORT extern void wiiuse_set_timeout(struct wiimote_t** wm, int wiimotes, byte normal_timeout, byte exp_timeout);
WIIUSE_EXPORT extern void wiiuse_set_accel_threshold(struct wiimote_t* wm, int threshold);
/* io.c */
WIIUSE_EXPORT extern int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout);
WIIUSE_EXPORT extern int wiiuse_connect(struct wiimote_t** wm, int wiimotes);
WIIUSE_EXPORT extern void wiiuse_disconnect(struct wiimote_t* wm);
/* events.c */
WIIUSE_EXPORT extern int wiiuse_poll(struct wiimote_t** wm, int wiimotes);
/**
* @brief Poll Wiimotes, and call the provided callback with information
* on each Wiimote that had an event.
*
* Alternative to calling wiiuse_poll yourself, and provides the same
* information struct on all platforms.
*
* @return Number of wiimotes that had an event.
*/
WIIUSE_EXPORT extern int wiiuse_update(struct wiimote_t** wm, int wiimotes, wiiuse_update_cb callback);
/* ir.c */
WIIUSE_EXPORT extern void wiiuse_set_ir(struct wiimote_t* wm, int status);
WIIUSE_EXPORT extern void wiiuse_set_ir_vres(struct wiimote_t* wm, unsigned int x, unsigned int y);
WIIUSE_EXPORT extern void wiiuse_set_ir_position(struct wiimote_t* wm, enum ir_position_t pos);
WIIUSE_EXPORT extern void wiiuse_set_aspect_ratio(struct wiimote_t* wm, enum aspect_t aspect);
WIIUSE_EXPORT extern void wiiuse_set_ir_sensitivity(struct wiimote_t* wm, int level);
/* nunchuk.c */
WIIUSE_EXPORT extern void wiiuse_set_nunchuk_orient_threshold(struct wiimote_t* wm, float threshold);
WIIUSE_EXPORT extern void wiiuse_set_nunchuk_accel_threshold(struct wiimote_t* wm, int threshold);
/* wiiboard.c */
/* this function not currently implemented... */
WIIUSE_EXPORT extern void wiiuse_set_wii_board_calib(struct wiimote_t *wm);
WIIUSE_EXPORT extern void wiiuse_set_motion_plus(struct wiimote_t *wm, int status);
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* WIIUSE_H_INCLUDED */

View File

@@ -0,0 +1,440 @@
/*
* wiiuse
*
* Written By:
* Michael Laforest < para >
* Email: < thepara (--AT--) g m a i l [--DOT--] com >
*
* Copyright 2006-2007
*
* 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
* (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 General internal wiiuse stuff.
*
* Since Wiiuse is a library, wiiuse.h is a duplicate
* of the API header.
*
* The code that would normally go in that file, but
* which is not needed by third party developers,
* is put here.
*
* So wiiuse_internal.h is included by other files
* internally, wiiuse.h is included only here.
*/
#ifndef WIIUSE_INTERNAL_H_INCLUDED
#define WIIUSE_INTERNAL_H_INCLUDED
#ifndef WIIUSE_PLATFORM
#if defined(_WIN32)
#define WIIUSE_PLATFORM
#define WIIUSE_WIN32
#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
#endif
#ifdef WIIUSE_WIN32
#include <winsock2.h>
#endif
#ifdef WIIUSE_BLUEZ
#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"
#if defined(_MSC_VER)
/* MS compilers of pre-VC2010 versions don't have stdint.h
* and I can't get VC2010's stdint.h to compile nicely in
* WiiUse
*/
#include "wiiuse_msvcstdint.h"
#else
#include <stdint.h>
#endif
/********************
*
* Wiimote internal codes
*
********************/
/** @addtogroup internal_general Internal: API for General Internal Use */
/** @{ */
/* Communication channels */
#define WM_OUTPUT_CHANNEL 0x11
#define WM_INPUT_CHANNEL 0x13
#define WM_SET_REPORT 0x50
#define WM_SET_DATA 0xA0
/* commands */
#define WM_CMD_LED 0x11
#define WM_CMD_REPORT_TYPE 0x12
#define WM_CMD_RUMBLE 0x13
#define WM_CMD_IR 0x13
#define WM_CMD_CTRL_STATUS 0x15
#define WM_CMD_WRITE_DATA 0x16
#define WM_CMD_READ_DATA 0x17
#define WM_CMD_IR_2 0x1A
/* input report ids */
#define WM_RPT_CTRL_STATUS 0x20
#define WM_RPT_READ 0x21
#define WM_RPT_WRITE 0x22
#define WM_RPT_BTN 0x30
#define WM_RPT_BTN_ACC 0x31
#define WM_RPT_BTN_ACC_IR 0x33
#define WM_RPT_BTN_EXP 0x34
#define WM_RPT_BTN_ACC_EXP 0x35
#define WM_RPT_BTN_IR_EXP 0x36
#define WM_RPT_BTN_ACC_IR_EXP 0x37
#define WM_BT_INPUT 0x01
#define WM_BT_OUTPUT 0x02
/* Identify the wiimote device by its class */
/* (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
/* controller status stuff */
#define WM_MAX_BATTERY_CODE 0xC8
/* offsets in wiimote memory */
#define WM_MEM_OFFSET_CALIBRATION 0x16
#define WM_EXP_MEM_BASE 0x04A40000
#define WM_EXP_ID 0x04A400FA
#define WM_EXP_MEM_ENABLE 0x04A40040
#define WM_EXP_MEM_ENABLE1 0x04A400F0
#define WM_EXP_MEM_ENABLE2 0x04A400FB
#define WM_EXP_MEM_CALIBR 0x04A40020
#define WM_EXP_MOTION_PLUS_IDENT 0x04A600FA
#define WM_EXP_MOTION_PLUS_ENABLE 0x04A600FE
#define WM_EXP_MOTION_PLUS_INIT 0x04A600F0
#define WM_REG_IR 0x04B00030
#define WM_REG_IR_BLOCK1 0x04B00000
#define WM_REG_IR_BLOCK2 0x04B0001A
#define WM_REG_IR_MODENUM 0x04B00033
#define WM_IR_TYPE_BASIC 0x01
#define WM_IR_TYPE_EXTENDED 0x03
/* controller status flags for the first message byte */
/* bit 1 is unknown */
#define WM_CTRL_STATUS_BYTE1_ATTACHMENT 0x02
#define WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED 0x04
#define WM_CTRL_STATUS_BYTE1_IR_ENABLED 0x08
#define WM_CTRL_STATUS_BYTE1_LED_1 0x10
#define WM_CTRL_STATUS_BYTE1_LED_2 0x20
#define WM_CTRL_STATUS_BYTE1_LED_3 0x40
#define WM_CTRL_STATUS_BYTE1_LED_4 0x80
/* aspect ratio */
#define WM_ASPECT_16_9_X 660
#define WM_ASPECT_16_9_Y 370
#define WM_ASPECT_4_3_X 560
#define WM_ASPECT_4_3_Y 420
/**
* Expansion stuff
*/
/* decrypted expansion id codes (located at 0x04A400FC) */
#define EXP_ID_CODE_NUNCHUK 0xA4200000
#define EXP_ID_CODE_WII_BOARD 0xA4200402
#define EXP_ID_CODE_CLASSIC_CONTROLLER 0xA4200101
#define EXP_ID_CODE_GUITAR 0xA4200103
#define EXP_ID_CODE_MOTION_PLUS 0xA4200405
#define EXP_ID_CODE_MOTION_PLUS_NUNCHUK 0xA4200505 /** Motion Plus ID in Nunchuck passthrough mode */
#define EXP_ID_CODE_MOTION_PLUS_CLASSIC 0xA4200705 /** Motion Plus ID in Classic control. passthrough */
/* decrypted M+ codes at 0x04A600FA */
#define EXP_ID_CODE_INACTIVE_MOTION_PLUS 0xA6200005 /** Inactive Motion Plus ID */
#define EXP_ID_CODE_INACTIVE_MOTION_PLUS_BUILTIN 0xA4200005 /** Inactive Motion Plus ID in Wii Remote Plus */
#define EXP_ID_CODE_NLA_MOTION_PLUS 0xA6200405 /** No longer active Motion Plus ID */
#define EXP_ID_CODE_NLA_MOTION_PLUS_NUNCHUK 0xA6200505 /** No longer active Motion Plus ID in Nunchuck passthrough mode */
#define EXP_ID_CODE_NLA_MOTION_PLUS_CLASSIC 0xA6200705 /** No longer active Motion Plus ID in Classic control. passthrough */
#define EXP_HANDSHAKE_LEN 224
/********************
*
* End Wiimote internal codes
*
********************/
#define WIIMOTE_INIT_STATES (WIIMOTE_STATE_IR_SENS_LVL3)
/* macro to manage states */
#define WIIMOTE_ENABLE_STATE(wm, s) (wm->state |= (s))
#define WIIMOTE_DISABLE_STATE(wm, s) (wm->state &= ~(s))
#define WIIMOTE_TOGGLE_STATE(wm, s) ((wm->state & (s)) ? WIIMOTE_DISABLE_STATE(wm, s) : WIIMOTE_ENABLE_STATE(wm, s))
#define WIIMOTE_IS_FLAG_SET(wm, s) ((wm->flags & (s)) == (s))
#define WIIMOTE_ENABLE_FLAG(wm, s) (wm->flags |= (s))
#define WIIMOTE_DISABLE_FLAG(wm, s) (wm->flags &= ~(s))
#define WIIMOTE_TOGGLE_FLAG(wm, s) ((wm->flags & (s)) ? WIIMOTE_DISABLE_FLAG(wm, s) : WIIMOTE_ENABLE_FLAG(wm, s))
#define NUNCHUK_IS_FLAG_SET(wm, s) ((*(wm->flags) & (s)) == (s))
/*
* Largest known payload is 21 bytes.
* Add 1 (Win32) or 2 (Mac, *nix) for the prefix and round up to a power of 2.
*/
#define MAX_PAYLOAD 32
/*
* Smooth tilt calculations are computed with the
* exponential moving average formula:
* St = St_last + (alpha * (tilt - St_last))
* alpha is between 0 and 1
*/
#define WIIUSE_DEFAULT_SMOOTH_ALPHA 0.07f
#define SMOOTH_ROLL 0x01
#define SMOOTH_PITCH 0x02
/** @} */
#include "wiiuse.h"
/** @addtogroup internal_general */
/** @{ */
#define _STRINGIFY(s) _STRINGIFY_IMPL(s)
#define _STRINGIFY_IMPL(s) #s
/* wiiuse version, from public per-component version defines */
#define WIIUSE_VERSION _STRINGIFY(WIIUSE_MAJOR) "." _STRINGIFY(WIIUSE_MINOR) "." _STRINGIFY(WIIUSE_MICRO)
#ifdef _MSC_VER
# define INLINE_UTIL __inline
#else
# define INLINE_UTIL static inline
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* not part of the api */
/** @brief Cross-platform call to sleep for at least the specified number
* of milliseconds.
*
* Use instead of Sleep(), usleep(), or similar functions.
* Defined in util.c
*/
void wiiuse_millisleep(int durationMilliseconds);
int wiiuse_set_report_type(struct wiimote_t* wm);
void wiiuse_send_next_pending_read_request(struct wiimote_t* wm);
void wiiuse_send_next_pending_write_request(struct wiimote_t* wm);
int wiiuse_send(struct wiimote_t* wm, byte report_type, byte* msg, int len);
int wiiuse_read_data_cb(struct wiimote_t* wm, wiiuse_read_cb read_cb, byte* buffer, unsigned int offset, uint16_t len);
int wiiuse_write_data_cb(struct wiimote_t *wm, unsigned int addr, byte* data, byte len, wiiuse_write_cb write_cb);
#ifdef WIIUSE_DOXYGEN_PARSING
/** @addtogroup betosystem Big-endian buffer to system-byte-order value
@{ */
/** @brief Given a buffer buf, copy and return a value of type uint8_t.
*/
uint8_t from_big_endian_uint8_t(byte * buf);
/** @brief Given a buffer buf, copy out a uint16_t, convert it from big-endian
to system byte order, and return it.
@note Requires that at least 2 bytes be available in buf, but does not
check this - it is your responsibility.
*/
uint16_t from_big_endian_uint16_t(byte * buf);
/** @brief Given a buffer buf, copy out a uint32_t, convert it from big-endian
to system byte order, and return it.
@note Requires that at least 4 bytes be available in buf, but does not
check this - it is your responsibility.
*/
uint32_t from_big_endian_uint32_t(byte * buf);
/** @} */
/** @addtogroup systemtobe System-byte-order value to big-endian buffer
@{
*/
/** @brief Copies the value val into the buffer buf.
@note Requires that at least 1 byte is available in buf, but does not
check this - it is your responsibility.
*/
void to_big_endian_uint8_t(byte * buf, uint8_t val);
/** @brief Converts the value val from system byte order to big endian,
and copies it into the given buffer starting at buf.
@note Requires that at least 2 bytes be available in buf, but does not
check this - it is your responsibility.
*/
void to_big_endian_uint16_t(byte * buf, uint16_t val);
/** @brief Converts the value val from system byte order to big endian,
and copies it into the given buffer starting at buf.
@note Requires that at least 4 bytes be available in buf, but does not
check this - it is your responsibility.
*/
void to_big_endian_uint32_t(byte * buf, uint32_t val);
/** @}
*/
/** @addtogroup bufferfunc Buffering functions
@brief These wrap around from/to_big_endian_TYPE, but take a byte** so that
they can advance the input/output pointer appropriately.
@{
*/
/** @brief Converts the value val from system byte order to big endian,
copies it into the given buffer starting at *buf, and advances buf by
sizeof(uint16_t).
*/
void buffer_big_endian_uint16_t(byte ** buf, uint16_t val);
/** @brief Given the address of a buffer pointer buf, copy out a uint16_t
from *buf, convert it from big-endian to system byte order, advance
buf by sizeof(uint16_t), and return the value retrieved.
*/
uint16_t unbuffer_big_endian_uint16_t(byte ** buf);
/** @sa buffer_big_endian_uint16_t()
*/
void buffer_big_endian_uint8_t(byte ** buf, uint8_t val);
/** @sa unbuffer_big_endian_uint8_t
*/
uint8_t unbuffer_big_endian_uint8_t(byte ** buf);
/** @sa buffer_big_endian_uint16_t
*/
void buffer_big_endian_uint32_t(byte ** buf, uint32_t val);
/** @sa unbuffer_big_endian_uint32_t
*/
uint8_t unbuffer_big_endian_uint32_t(byte ** buf)
/** @} */
#else /* this else is true when not in doxygen */
INLINE_UTIL void to_big_endian_uint8_t(byte * buf, uint8_t val) {
memcpy(buf, &val, 1);
}
INLINE_UTIL uint8_t from_big_endian_uint8_t(byte * buf) {
uint8_t beVal;
memcpy(&beVal, buf, 1);
return beVal;
}
#define WIIUSE_DECLARE_ENDIAN_CONVERSION_OPS(_TYPE, _TOBE, _FROMBE) \
INLINE_UTIL void to_big_endian_##_TYPE(byte * buf, _TYPE val) { \
_TYPE beVal = _TOBE(val); \
memcpy(buf, &beVal, sizeof(_TYPE)); \
} \
INLINE_UTIL _TYPE from_big_endian_##_TYPE(byte * buf) { \
_TYPE beVal; \
memcpy(&beVal, buf, sizeof(_TYPE)); \
return _FROMBE(beVal); \
}
WIIUSE_DECLARE_ENDIAN_CONVERSION_OPS(uint16_t, htons, ntohs)
WIIUSE_DECLARE_ENDIAN_CONVERSION_OPS(uint32_t, htonl, ntohl)
#undef WIIUSE_DECLARE_ENDIAN_CONVERSION_OPS
#define WIIUSE_DECLARE_BUFFERING_OPS(_TYPE) \
INLINE_UTIL void buffer_big_endian_##_TYPE (byte ** buf, _TYPE val) { \
to_big_endian_##_TYPE(*buf, val); \
*buf += sizeof(_TYPE); \
} \
INLINE_UTIL _TYPE unbuffer_big_endian_##_TYPE (byte ** buf) { \
byte * current = *buf; \
*buf += sizeof(_TYPE); \
return from_big_endian_##_TYPE(current); \
}
WIIUSE_DECLARE_BUFFERING_OPS(uint8_t)
WIIUSE_DECLARE_BUFFERING_OPS(uint16_t)
WIIUSE_DECLARE_BUFFERING_OPS(uint32_t)
#undef WIIUSE_DECLARE_BUFFERING_OPS
#endif /* not in doxygen */
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* WIIUSE_INTERNAL_H_INCLUDED */

View File

@@ -0,0 +1,226 @@
// ISO C9x compliant stdint.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
// Copyright (c) 2006-2008 Alexander Chemeris
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The name of the author may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_STDINT_H_ // [
#define _MSC_STDINT_H_
#if _MSC_VER > 1000
#pragma once
#endif
#include <limits.h>
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
// or compiler give many errors like this:
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
#ifdef __cplusplus
extern "C" {
#endif
# include <wchar.h>
#ifdef __cplusplus
}
#endif
// Define _W64 macros to mark types changing their size, like intptr_t.
#ifndef _W64
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
# define _W64 __w64
# else
# define _W64
# endif
#endif
// 7.18.1 Integer types
// 7.18.1.1 Exact-width integer types
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
// realize that, e.g. char has the same size as __int8
// so we give up on __intX for them.
#if (_MSC_VER < 1300)
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#else
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
#endif
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
// 7.18.1.4 Integer types capable of holding object pointers
#ifdef _WIN64 // [
typedef signed __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else // _WIN64 ][
typedef _W64 signed int intptr_t;
typedef _W64 unsigned int uintptr_t;
#endif // _WIN64 ]
// 7.18.1.5 Greatest-width integer types
typedef int64_t intmax_t;
typedef uint64_t uintmax_t;
// 7.18.2 Limits of specified-width integer types
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
// 7.18.2.1 Limits of exact-width integer types
#define INT8_MIN ((int8_t)_I8_MIN)
#define INT8_MAX _I8_MAX
#define INT16_MIN ((int16_t)_I16_MIN)
#define INT16_MAX _I16_MAX
#define INT32_MIN ((int32_t)_I32_MIN)
#define INT32_MAX _I32_MAX
#define INT64_MIN ((int64_t)_I64_MIN)
#define INT64_MAX _I64_MAX
#define UINT8_MAX _UI8_MAX
#define UINT16_MAX _UI16_MAX
#define UINT32_MAX _UI32_MAX
#define UINT64_MAX _UI64_MAX
// 7.18.2.2 Limits of minimum-width integer types
#define INT_LEAST8_MIN INT8_MIN
#define INT_LEAST8_MAX INT8_MAX
#define INT_LEAST16_MIN INT16_MIN
#define INT_LEAST16_MAX INT16_MAX
#define INT_LEAST32_MIN INT32_MIN
#define INT_LEAST32_MAX INT32_MAX
#define INT_LEAST64_MIN INT64_MIN
#define INT_LEAST64_MAX INT64_MAX
#define UINT_LEAST8_MAX UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX
// 7.18.2.3 Limits of fastest minimum-width integer types
#define INT_FAST8_MIN INT8_MIN
#define INT_FAST8_MAX INT8_MAX
#define INT_FAST16_MIN INT16_MIN
#define INT_FAST16_MAX INT16_MAX
#define INT_FAST32_MIN INT32_MIN
#define INT_FAST32_MAX INT32_MAX
#define INT_FAST64_MIN INT64_MIN
#define INT_FAST64_MAX INT64_MAX
#define UINT_FAST8_MAX UINT8_MAX
#define UINT_FAST16_MAX UINT16_MAX
#define UINT_FAST32_MAX UINT32_MAX
#define UINT_FAST64_MAX UINT64_MAX
// 7.18.2.4 Limits of integer types capable of holding object pointers
#ifdef _WIN64 // [
# define INTPTR_MIN INT64_MIN
# define INTPTR_MAX INT64_MAX
# define UINTPTR_MAX UINT64_MAX
#else // _WIN64 ][
# define INTPTR_MIN INT32_MIN
# define INTPTR_MAX INT32_MAX
# define UINTPTR_MAX UINT32_MAX
#endif // _WIN64 ]
// 7.18.2.5 Limits of greatest-width integer types
#define INTMAX_MIN INT64_MIN
#define INTMAX_MAX INT64_MAX
#define UINTMAX_MAX UINT64_MAX
// 7.18.3 Limits of other integer types
#ifdef _WIN64 // [
# define PTRDIFF_MIN _I64_MIN
# define PTRDIFF_MAX _I64_MAX
#else // _WIN64 ][
# define PTRDIFF_MIN _I32_MIN
# define PTRDIFF_MAX _I32_MAX
#endif // _WIN64 ]
#define SIG_ATOMIC_MIN INT_MIN
#define SIG_ATOMIC_MAX INT_MAX
#ifndef SIZE_MAX // [
# ifdef _WIN64 // [
# define SIZE_MAX _UI64_MAX
# else // _WIN64 ][
# define SIZE_MAX _UI32_MAX
# endif // _WIN64 ]
#endif // SIZE_MAX ]
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
#ifndef WCHAR_MIN // [
# define WCHAR_MIN 0
#endif // WCHAR_MIN ]
#ifndef WCHAR_MAX // [
# define WCHAR_MAX _UI16_MAX
#endif // WCHAR_MAX ]
#define WINT_MIN 0
#define WINT_MAX _UI16_MAX
#endif // __STDC_LIMIT_MACROS ]
// 7.18.4 Limits of other integer types
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
// 7.18.4.1 Macros for minimum-width integer constants
#define INT8_C(val) val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val) val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
// 7.18.4.2 Macros for greatest-width integer constants
#define INTMAX_C INT64_C
#define UINTMAX_C UINT64_C
#endif // __STDC_CONSTANT_MACROS ]
#endif // _MSC_STDINT_H_ ]