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