372 lines
13 KiB
C++
372 lines
13 KiB
C++
// Copyright (C) 2003 Dolphin Project.
|
|
|
|
// 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, version 2.0.
|
|
|
|
// 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 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
#include <iostream> // System
|
|
|
|
#include "wiiuse.h" // Externals
|
|
|
|
#include "StringUtil.h"
|
|
#include "Timer.h"
|
|
#include "pluginspecs_wiimote.h"
|
|
|
|
#include "wiimote_real.h" // Local
|
|
#include "wiimote_hid.h"
|
|
#include "EmuDefinitions.h"
|
|
#include "EmuMain.h"
|
|
#include "main.h"
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
#include "ConfigBasicDlg.h"
|
|
#include "ConfigRecordingDlg.h"
|
|
#include "ConfigPadDlg.h"
|
|
#endif
|
|
#include "Config.h"
|
|
|
|
namespace WiiMoteReal
|
|
{
|
|
int GetIRDataSize(struct wiimote_t* wm)
|
|
{
|
|
if (WIIUSE_USING_EXP(wm))
|
|
return 10;
|
|
else
|
|
return 12;
|
|
}
|
|
|
|
void handle_ctrl_status(struct wiimote_t* wm)
|
|
{
|
|
DEBUG_LOG(WIIMOTE, "--- CONTROLLER STATUS [wiimote id %i] ---", wm->unid);
|
|
|
|
DEBUG_LOG(WIIMOTE, "attachment: %i", wm->exp.type);
|
|
DEBUG_LOG(WIIMOTE, "speaker: %i", WIIUSE_USING_SPEAKER(wm));
|
|
DEBUG_LOG(WIIMOTE, "ir: %i", WIIUSE_USING_IR(wm));
|
|
DEBUG_LOG(WIIMOTE, "leds: %i %i %i %i", WIIUSE_IS_LED_SET(wm, 1), WIIUSE_IS_LED_SET(wm, 2), WIIUSE_IS_LED_SET(wm, 3), WIIUSE_IS_LED_SET(wm, 4));
|
|
DEBUG_LOG(WIIMOTE, "battery: %f %%", wm->battery_level);
|
|
}
|
|
|
|
bool IRDataOK(struct wiimote_t* wm)
|
|
{
|
|
// This check is valid because 0 should only be returned if the data
|
|
// hasn't been filled in by wiiuse
|
|
int IRDataSize = GetIRDataSize(wm);
|
|
for (int i = 7; i < IRDataSize; i++)
|
|
if (wm->event_buf[i] == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void handle_event(struct wiimote_t* wm)
|
|
{
|
|
//DEBUG_LOG(WIIMOTE, "--- EVENT [id %i] ---", wm->unid);
|
|
|
|
// if a button is pressed, report it
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_A)) DEBUG_LOG(WIIMOTE, "A pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_B)) DEBUG_LOG(WIIMOTE, "B pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_UP)) DEBUG_LOG(WIIMOTE, "UP pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_DOWN)) DEBUG_LOG(WIIMOTE, "DOWN pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_LEFT)) DEBUG_LOG(WIIMOTE, "LEFT pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_RIGHT)) DEBUG_LOG(WIIMOTE, "RIGHT pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_MINUS)) DEBUG_LOG(WIIMOTE, "MINUS pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_PLUS)) DEBUG_LOG(WIIMOTE, "PLUS pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_ONE)) DEBUG_LOG(WIIMOTE, "ONE pressed");
|
|
//if (IS_PRESSED(wm, WIIMOTE_BUTTON_ONE)) g_Run = false;
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_TWO)) DEBUG_LOG(WIIMOTE, "TWO pressed");
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_HOME)) DEBUG_LOG(WIIMOTE, "HOME pressed");
|
|
|
|
|
|
// Pressing minus will tell the wiimote we are no longer interested in movement.
|
|
// This is useful because it saves battery power.
|
|
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_MINUS))
|
|
{
|
|
wiiuse_motion_sensing(wm, 0);
|
|
wiiuse_set_ir(wm, 0);
|
|
g_MotionSensing = false;
|
|
}
|
|
// Turn aceelerometer and IR reporting on, there is some kind of bug that prevents us from turing these on
|
|
// directly after each other, so we have to wait for another wiiuse_poll() this way
|
|
if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_PLUS))
|
|
{
|
|
wiiuse_motion_sensing(wm, 1);
|
|
g_MotionSensing = true;
|
|
}
|
|
// Turn IR reporting on
|
|
if (g_MotionSensing && !WIIUSE_USING_IR(wm))
|
|
wiiuse_set_ir(wm, 1);
|
|
|
|
// Print battery status
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
if(m_RecordingConfigFrame && g_Config.bUpdateRealWiimote)
|
|
m_RecordingConfigFrame->m_GaugeBattery->SetValue((int)floor((wm->battery_level * 100) + 0.5));
|
|
#endif
|
|
// Create shortcut to the nunchuck
|
|
struct nunchuk_t* nc = NULL;
|
|
if (wm->exp.type == EXP_NUNCHUK)
|
|
nc = (nunchuk_t*)&wm->exp.nunchuk;
|
|
|
|
// If the accelerometer is turned on then print angles
|
|
if (WIIUSE_USING_ACC(wm) && WIIUSE_USING_IR(wm))
|
|
{
|
|
std::string Tmp;
|
|
Tmp += StringFromFormat("Roll: %2.1f ", wm->orient.roll);
|
|
Tmp += StringFromFormat("Pitch: %2.1f ", wm->orient.pitch);
|
|
Tmp += StringFromFormat("Battery: %1.2f\n", wm->battery_level);
|
|
Tmp += StringFromFormat("G-Force x, y, z: %1.2f %1.2f %1.2f\n", wm->gforce.x, wm->gforce.y, wm->gforce.z);
|
|
Tmp += StringFromFormat("Accel x, y, z: %03i %03i %03i\n", wm->accel.x, wm->accel.y, wm->accel.z);
|
|
|
|
// wm->event_buf is cleared at the end of all wiiuse_poll(), so wm->event_buf will always be zero
|
|
// after that. To get the raw IR data we need to read the wiimote again. This seems to work most of the time,
|
|
// it seems to fails with a regular interval about each tenth read.
|
|
if (wiiuse_io_read(wm))
|
|
if (IRDataOK(wm))
|
|
memcpy(g_EventBuffer, wm->event_buf, GetIRDataSize(wm));
|
|
|
|
// Go through each of the 4 possible IR sources
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
// Check if the source is visible
|
|
if (wm->ir.dot[i].visible)
|
|
Tmp += StringFromFormat("IR source %i: (%u, %u)\n", i, wm->ir.dot[i].x, wm->ir.dot[i].y);
|
|
}
|
|
|
|
Tmp += "\n";
|
|
Tmp += StringFromFormat("IR cursor: (%u, %u)\n", wm->ir.x, wm->ir.y);
|
|
Tmp += StringFromFormat("IR z distance: %f\n", wm->ir.z);
|
|
|
|
if(wm->exp.type == EXP_NUNCHUK)
|
|
{
|
|
Tmp += "\n";
|
|
Tmp += StringFromFormat("Nunchuck accel x, y, z: %03i %03i %03i\n", nc->accel.x, nc->accel.y, nc->accel.z);
|
|
}
|
|
|
|
//Tmp += "\n";
|
|
//std::string TmpData = ArrayToString(g_EventBuffer, ReportSize, 0, 30);
|
|
//Tmp += "Data: " + TmpData;
|
|
|
|
//DEBUG_LOG(WIIMOTE, "%s", Tmp.c_str());
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
if(m_RecordingConfigFrame)
|
|
{
|
|
// Produce adjusted accelerometer values
|
|
float _Gx = (float)(wm->accel.x - wm->accel_calib.cal_zero.x) / (float)wm->accel_calib.cal_g.x;
|
|
float _Gy = (float)(wm->accel.y - wm->accel_calib.cal_zero.y) / (float)wm->accel_calib.cal_g.y;
|
|
float _Gz = (float)(wm->accel.z - wm->accel_calib.cal_zero.z) / (float)wm->accel_calib.cal_g.z;
|
|
|
|
// Conver the data to integers
|
|
int Gx = (int)(_Gx * 100);
|
|
int Gy = (int)(_Gy * 100);
|
|
int Gz = (int)(_Gz * 100);
|
|
|
|
// And for the Nunchuck
|
|
u8 AccelNX = 0, AccelNY = 0, AccelNZ = 0;
|
|
if(wm->exp.type == EXP_NUNCHUK)
|
|
{
|
|
if((nc->accel.x + g_Config.iAccNunNeutralX) <= 255) AccelNX = nc->accel.x + g_Config.iAccNunNeutralX;
|
|
if((nc->accel.y + g_Config.iAccNunNeutralY) <= 255) AccelNY = nc->accel.y + g_Config.iAccNunNeutralY;
|
|
if((nc->accel.z + g_Config.iAccNunNeutralZ) <= 255) AccelNZ = nc->accel.z + g_Config.iAccNunNeutralZ;
|
|
}
|
|
|
|
if(g_Config.bUpdateRealWiimote)
|
|
{
|
|
// Update gauges
|
|
m_RecordingConfigFrame->m_GaugeRoll[0]->SetValue(wm->orient.roll + 180);
|
|
m_RecordingConfigFrame->m_GaugeRoll[1]->SetValue(wm->orient.pitch + 180);
|
|
|
|
// Show g. forces between -3 and 3
|
|
m_RecordingConfigFrame->m_GaugeGForce[0]->SetValue((int)floor((wm->gforce.x * 100) + 300.5));
|
|
m_RecordingConfigFrame->m_GaugeGForce[1]->SetValue((int)floor((wm->gforce.y * 100) + 300.5));
|
|
m_RecordingConfigFrame->m_GaugeGForce[2]->SetValue((int)floor((wm->gforce.z * 100) + 300.5));
|
|
|
|
m_RecordingConfigFrame->m_GaugeAccel[0]->SetValue(wm->accel.x);
|
|
m_RecordingConfigFrame->m_GaugeAccel[1]->SetValue(wm->accel.y);
|
|
m_RecordingConfigFrame->m_GaugeAccel[2]->SetValue(wm->accel.z);
|
|
|
|
m_RecordingConfigFrame->m_TextIR->SetLabel(wxString::Format(
|
|
wxT("Cursor: %03u %03u\nDistance:%4.0f"), wm->ir.x, wm->ir.y, wm->ir.z));
|
|
|
|
//m_RecordingConfigFrame->m_TextAccNeutralCurrent->SetLabel(wxString::Format(
|
|
// wxT("Current: %03u %03u %03u"), Gx, Gy, Gz));
|
|
|
|
if(m_RecordingConfigFrame->m_bRecording)
|
|
DEBUG_LOG(WIIMOTE, "Wiiuse Recorded accel x, y, z: %03i %03i %03i", Gx, Gy, Gz);
|
|
}
|
|
|
|
// Send the data to be saved
|
|
m_RecordingConfigFrame->DoRecordMovement(Gx, Gy, Gz, g_EventBuffer + 6, GetIRDataSize(wm));
|
|
|
|
// Turn recording on and off
|
|
if (IS_PRESSED(wm, WIIMOTE_BUTTON_A)) m_RecordingConfigFrame->DoRecordA(true);
|
|
else m_RecordingConfigFrame->DoRecordA(false);
|
|
|
|
// ------------------------------------
|
|
// Show roll and pitch in the status box
|
|
// --------------
|
|
if(!g_DebugData)
|
|
{
|
|
DEBUG_LOG(WIIMOTE, "Roll:%03i Pitch:%03i", (int)wm->orient.roll, (int)wm->orient.pitch);
|
|
}
|
|
if(m_PadConfigFrame)
|
|
{
|
|
// Convert Roll and Pitch from 180 to 0x8000
|
|
int Roll = (int)wm->orient.roll * (0x8000 / 180);
|
|
int Pitch = (int)wm->orient.pitch * (0x8000 / 180);
|
|
// Convert it to the box
|
|
m_PadConfigFrame->Convert2Box(Roll);
|
|
m_PadConfigFrame->Convert2Box(Pitch);
|
|
// Show roll and pitch in the axis boxes
|
|
m_PadConfigFrame->m_bmpDotRightOut[0]->SetPosition(wxPoint(Roll, Pitch));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
// Otherwise remove the values
|
|
else
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
if (m_RecordingConfigFrame)
|
|
{
|
|
m_RecordingConfigFrame->m_GaugeRoll[0]->SetValue(0);
|
|
m_RecordingConfigFrame->m_GaugeRoll[1]->SetValue(0);
|
|
|
|
m_RecordingConfigFrame->m_GaugeGForce[0]->SetValue(0);
|
|
m_RecordingConfigFrame->m_GaugeGForce[1]->SetValue(0);
|
|
m_RecordingConfigFrame->m_GaugeGForce[2]->SetValue(0);
|
|
|
|
m_RecordingConfigFrame->m_GaugeAccel[0]->SetValue(0);
|
|
m_RecordingConfigFrame->m_GaugeAccel[1]->SetValue(0);
|
|
m_RecordingConfigFrame->m_GaugeAccel[2]->SetValue(0);
|
|
|
|
m_RecordingConfigFrame->m_TextIR->SetLabel(wxT("Cursor:\nDistance:"));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void ReadWiimote()
|
|
{
|
|
/* I place this outside wiiuse_poll() to produce a continous recording regardless of the status
|
|
change of the Wiimote, wiiuse_poll() is only true if the status has changed. However, this the
|
|
timing functions for recording playback that checks the time of the recording this should not
|
|
be needed. But I still use it becase it seemed like state_changed() or the threshold values or
|
|
something else might fail so that only huge status changed were reported. */
|
|
for (int i = 0; i < g_NumberOfWiiMotes; i++)
|
|
{
|
|
handle_event(g_WiiMotesFromWiiUse[i]);
|
|
}
|
|
|
|
// Declaration
|
|
std::string Temp;
|
|
|
|
/* Timeout for data reading. This is used in Initialize() to read the Eeprom, if we have not gotten
|
|
what we wanted in the WIIUSE_READ_DATA case we stop this loop and enable the regular
|
|
wiiuse_io_read() and wiiuse_io_write() loop again. */
|
|
if (g_RunTemporary)
|
|
{
|
|
// The SecondsToWait holds if the update rate of wiiuse_poll() is kept at the default value of 10 ms
|
|
static const int SecondsToWait = 2;
|
|
g_RunTemporaryCountdown++;
|
|
if(g_RunTemporaryCountdown > (SecondsToWait * 100))
|
|
{
|
|
g_RunTemporaryCountdown = 0;
|
|
g_RunTemporary = false;
|
|
}
|
|
}
|
|
|
|
// Read formatted Wiimote data
|
|
if (wiiuse_poll(g_WiiMotesFromWiiUse, MAX_WIIMOTES))
|
|
{
|
|
/*
|
|
* This happens if something happened on any wiimote.
|
|
* So go through each one and check if anything happened.
|
|
*/
|
|
int i = 0;
|
|
for (; i < MAX_WIIMOTES; ++i)
|
|
{
|
|
switch (g_WiiMotesFromWiiUse[i]->event)
|
|
{
|
|
case WIIUSE_EVENT:
|
|
/* a generic event occured */
|
|
//handle_event(g_WiiMotesFromWiiUse[i]);
|
|
break;
|
|
|
|
case WIIUSE_STATUS:
|
|
/* a status event occured */
|
|
//handle_ctrl_status(g_WiiMotesFromWiiUse[i]);
|
|
break;
|
|
|
|
case WIIUSE_DISCONNECT:
|
|
case WIIUSE_UNEXPECTED_DISCONNECT:
|
|
/* the wiimote disconnected */
|
|
//handle_disconnect(wiimotes[i]);
|
|
break;
|
|
|
|
case WIIUSE_READ_DATA:
|
|
/*
|
|
* Data we requested to read was returned.
|
|
* Take a look at wiimotes[i]->read_req
|
|
* for the data.
|
|
*/
|
|
if(g_WiiMotesFromWiiUse[i]->read_req->size == sizeof(WiiMoteEmu::EepromData_0)
|
|
&& g_WiiMotesFromWiiUse[i]->read_req->addr == 0)
|
|
{
|
|
Temp = ArrayToString(g_WiiMotesFromWiiUse[i]->read_req->buf, sizeof(WiiMoteEmu::EepromData_0), 0, 30);
|
|
memcpy(WiiMoteEmu::g_Eeprom, g_WiiMotesFromWiiUse[i]->read_req->buf, sizeof(WiiMoteEmu::EepromData_0));
|
|
DEBUG_LOG(WIIMOTE, "EEPROM: %s", Temp.c_str());
|
|
WiiMoteEmu::UpdateEeprom();
|
|
g_RunTemporary = false;
|
|
}
|
|
break;
|
|
|
|
case WIIUSE_NUNCHUK_INSERTED:
|
|
/*
|
|
* a nunchuk was inserted
|
|
* This is a good place to set any nunchuk specific
|
|
* threshold values. By default they are the same
|
|
* as the wiimote.
|
|
*/
|
|
//wiiuse_set_nunchuk_orient_threshold((struct nunchuk_t*)&wiimotes[i]->exp.nunchuk, 90.0f);
|
|
//wiiuse_set_nunchuk_accel_threshold((struct nunchuk_t*)&wiimotes[i]->exp.nunchuk, 100);
|
|
DEBUG_LOG(WIIMOTE, "Nunchuk inserted.");
|
|
break;
|
|
|
|
case WIIUSE_CLASSIC_CTRL_INSERTED:
|
|
DEBUG_LOG(WIIMOTE, "Classic controller inserted.");
|
|
break;
|
|
|
|
case WIIUSE_GUITAR_HERO_3_CTRL_INSERTED:
|
|
// some expansion was inserted
|
|
//handle_ctrl_status(wiimotes[i]);
|
|
DEBUG_LOG(WIIMOTE, "Guitar Hero 3 controller inserted.");
|
|
break;
|
|
|
|
case WIIUSE_NUNCHUK_REMOVED:
|
|
case WIIUSE_CLASSIC_CTRL_REMOVED:
|
|
case WIIUSE_GUITAR_HERO_3_CTRL_REMOVED:
|
|
// some expansion was removed
|
|
//handle_ctrl_status(wiimotes[i]);
|
|
DEBUG_LOG(WIIMOTE, "An expansion was removed.");
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}; // end of namespace
|