mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-25 23:41:19 +01:00
fcdd2a8e17
1. Fixed the dual mode. You should now be able to change between the real and emulated Wiimote at any time, even when the Nunchuck is connected. It also supports third party Wireless Nunchucks that never sends any calibration values. The Nunchuck status should be automatically updated. The Nunchuck stick may get stuck, but that should fix itself if you disconnect and reconnect again. The only important problems seems to be that the real Wiimote fails to answer sometimes so that the Core functions disconnect it. 2. Began looking at how to reconnect the Wiimote after an unwanted HCI disconnect command git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2129 8ced0084-cf51-0410-be5f-012b33b47a6e
481 lines
13 KiB
C++
481 lines
13 KiB
C++
// Copyright (C) 2003-2008 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/
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Includes
|
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
|
|
#include <iostream> // System
|
|
#include "pluginspecs_wiimote.h"
|
|
|
|
#include "wiiuse.h"
|
|
#include <queue>
|
|
|
|
#include "Common.h"
|
|
#include "Thread.h"
|
|
#include "StringUtil.h"
|
|
#include "ConsoleWindow.h"
|
|
#include "Timer.h"
|
|
|
|
#include "wiimote_hid.h"
|
|
#include "main.h"
|
|
#include "Config.h"
|
|
#include "EmuMain.h"
|
|
#include "EmuDefinitions.h"
|
|
#define EXCLUDE_H // Avoid certain declarations in wiimote_real.h
|
|
#include "wiimote_real.h"
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
#include "ConfigDlg.h"
|
|
#endif
|
|
|
|
extern SWiimoteInitialize g_WiimoteInitialize;
|
|
////////////////////////////////////////
|
|
|
|
|
|
namespace WiiMoteReal
|
|
{
|
|
|
|
//******************************************************************************
|
|
// Forwarding
|
|
//******************************************************************************
|
|
|
|
class CWiiMote;
|
|
|
|
#ifdef _WIN32
|
|
DWORD WINAPI ReadWiimote_ThreadFunc(void* arg);
|
|
#else
|
|
void* ReadWiimote_ThreadFunc(void* arg);
|
|
#endif
|
|
//******************************************************************************
|
|
// Variable declarations
|
|
//******************************************************************************
|
|
|
|
wiimote_t** g_WiiMotesFromWiiUse = NULL;
|
|
Common::Thread* g_pReadThread = NULL;
|
|
int g_NumberOfWiiMotes;
|
|
CWiiMote* g_WiiMotes[MAX_WIIMOTES];
|
|
bool g_Shutdown = false;
|
|
bool g_ThreadGoing = false;
|
|
bool g_LocalThread = true;
|
|
bool g_IRSensing = false;
|
|
bool g_MotionSensing = false;
|
|
u64 g_UpdateTime = 0;
|
|
int g_UpdateCounter = 0;
|
|
bool g_RunTemporary = false;
|
|
int g_RunTemporaryCountdown = 0;
|
|
u8 g_EventBuffer[32];
|
|
|
|
//******************************************************************************
|
|
// Probably this class should be in its own file
|
|
//******************************************************************************
|
|
|
|
class CWiiMote
|
|
{
|
|
public:
|
|
|
|
//////////////////////////////////////////
|
|
// On create and on uncreate
|
|
// ---------------
|
|
CWiiMote(u8 _WiimoteNumber, wiimote_t* _pWiimote)
|
|
: m_WiimoteNumber(_WiimoteNumber)
|
|
, m_channelID(0)
|
|
, m_pWiiMote(_pWiimote)
|
|
, m_pCriticalSection(NULL)
|
|
, m_LastReportValid(false)
|
|
{
|
|
m_pCriticalSection = new Common::CriticalSection();
|
|
|
|
//wiiuse_set_leds(m_pWiiMote, WIIMOTE_LED_4);
|
|
|
|
#ifdef _WIN32
|
|
// F|RES: i dunno if we really need this
|
|
CancelIo(m_pWiiMote->dev_handle);
|
|
#endif
|
|
}
|
|
|
|
virtual ~CWiiMote()
|
|
{
|
|
delete m_pCriticalSection;
|
|
};
|
|
//////////////////////
|
|
|
|
|
|
//////////////////////////////////////////
|
|
// Queue raw HID data from the core to the wiimote
|
|
// ---------------
|
|
void SendData(u16 _channelID, const u8* _pData, u32 _Size)
|
|
{
|
|
m_channelID = _channelID;
|
|
|
|
m_pCriticalSection->Enter();
|
|
{
|
|
SEvent WriteEvent;
|
|
memcpy(WriteEvent.m_PayLoad, _pData + 1, _Size - 1);
|
|
m_EventWriteQueue.push(WriteEvent);
|
|
|
|
// Debugging
|
|
//std::string Temp = ArrayToString(WriteEvent.m_PayLoad, 28, 0, 30);
|
|
//Console::Print("Wiimote Write:\n%s\n", Temp.c_str());
|
|
}
|
|
m_pCriticalSection->Leave();
|
|
}
|
|
/////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Read and write data to the Wiimote */
|
|
// ---------------
|
|
void ReadData()
|
|
{
|
|
m_pCriticalSection->Enter();
|
|
|
|
// Send data to the Wiimote
|
|
if (!m_EventWriteQueue.empty())
|
|
{
|
|
//Console::Print("Writing data to the Wiimote\n");
|
|
SEvent& rEvent = m_EventWriteQueue.front();
|
|
wiiuse_io_write(m_pWiiMote, (byte*)rEvent.m_PayLoad, MAX_PAYLOAD);
|
|
m_EventWriteQueue.pop();
|
|
|
|
#ifdef _WIN32
|
|
// Debugging. Move the data one step to the right first.
|
|
memcpy(rEvent.m_PayLoad + 1, rEvent.m_PayLoad, sizeof(rEvent.m_PayLoad) - 1);
|
|
rEvent.m_PayLoad[0] = 0xa2;
|
|
InterruptDebugging(false, rEvent.m_PayLoad);
|
|
#endif
|
|
}
|
|
|
|
m_pCriticalSection->Leave();
|
|
|
|
// Read data from wiimote (but don't send it to the core, just filter and queue)
|
|
if (wiiuse_io_read(m_pWiiMote))
|
|
{
|
|
const byte* pBuffer = m_pWiiMote->event_buf;
|
|
|
|
// Check if we have a channel (connection) if so save the data...
|
|
if (m_channelID > 0)
|
|
{
|
|
m_pCriticalSection->Enter();
|
|
|
|
// Filter out data reports
|
|
if (pBuffer[0] >= 0x30)
|
|
{
|
|
// Copy Buffer to LastReport
|
|
memcpy(m_LastReport.m_PayLoad, pBuffer, MAX_PAYLOAD);
|
|
m_LastReportValid = true;
|
|
}
|
|
else
|
|
{
|
|
// Copy Buffer to ImportantEvent
|
|
SEvent ImportantEvent;
|
|
memcpy(ImportantEvent.m_PayLoad, pBuffer, MAX_PAYLOAD);
|
|
|
|
// Put it in the read queue right away
|
|
m_EventReadQueue.push(ImportantEvent);
|
|
}
|
|
m_pCriticalSection->Leave();
|
|
}
|
|
#ifdef _WIN32
|
|
/* Debugging
|
|
//if(GetAsyncKeyState('V'))
|
|
{
|
|
std::string Temp = ArrayToString(pBuffer, 20, 0, 30);
|
|
Console::Print("Data: %s\n", Temp.c_str());
|
|
} */
|
|
#endif
|
|
}
|
|
};
|
|
/////////////////////
|
|
|
|
|
|
//////////////////////////////////////////
|
|
// Send queued data to the core
|
|
// ---------------
|
|
void Update()
|
|
{
|
|
// Thread function
|
|
m_pCriticalSection->Enter();
|
|
|
|
if (m_EventReadQueue.empty())
|
|
{
|
|
// Send the data report
|
|
if (m_LastReportValid) SendEvent(m_LastReport);
|
|
}
|
|
else
|
|
{
|
|
// Send a 0x20, 0x21 or 0x22 report
|
|
SendEvent(m_EventReadQueue.front());
|
|
m_EventReadQueue.pop();
|
|
}
|
|
|
|
m_pCriticalSection->Leave();
|
|
};
|
|
/////////////////////
|
|
|
|
|
|
//////////////////////////////////////////
|
|
// Clear events
|
|
// ---------------
|
|
void ClearEvents()
|
|
{
|
|
while (!m_EventReadQueue.empty())
|
|
m_EventReadQueue.pop();
|
|
while (!m_EventWriteQueue.empty())
|
|
m_EventWriteQueue.pop();
|
|
}
|
|
/////////////////////
|
|
|
|
private:
|
|
|
|
struct SEvent
|
|
{
|
|
SEvent()
|
|
{
|
|
memset(m_PayLoad, 0, MAX_PAYLOAD);
|
|
}
|
|
byte m_PayLoad[MAX_PAYLOAD];
|
|
};
|
|
typedef std::queue<SEvent> CEventQueue;
|
|
|
|
u8 m_WiimoteNumber; // Just for debugging
|
|
u16 m_channelID;
|
|
CEventQueue m_EventReadQueue; // Read from Wiimote
|
|
CEventQueue m_EventWriteQueue; // Write to Wiimote
|
|
Common::CriticalSection* m_pCriticalSection;
|
|
bool m_LastReportValid;
|
|
SEvent m_LastReport;
|
|
wiimote_t* m_pWiiMote; // This is g_WiiMotesFromWiiUse[]
|
|
|
|
//////////////////////////////////////////
|
|
// Send queued data to the core
|
|
// ---------------
|
|
void SendEvent(SEvent& _rEvent)
|
|
{
|
|
// We don't have an answer channel
|
|
if (m_channelID == 0) return;
|
|
|
|
// Check event buffer
|
|
u8 Buffer[1024];
|
|
u32 Offset = 0;
|
|
hid_packet* pHidHeader = (hid_packet*)(Buffer + Offset);
|
|
Offset += sizeof(hid_packet);
|
|
pHidHeader->type = HID_TYPE_DATA;
|
|
pHidHeader->param = HID_PARAM_INPUT;
|
|
|
|
// Create the buffer
|
|
memcpy(&Buffer[Offset], _rEvent.m_PayLoad, MAX_PAYLOAD);
|
|
/* This Offset value is not exactly correct like it is for the emulated Wiimote reports. It's
|
|
often to big, but I guess that's okay. The game will know how big the actual data is. */
|
|
Offset += MAX_PAYLOAD;
|
|
|
|
// Send it
|
|
g_WiimoteInitialize.pWiimoteInput(m_channelID, Buffer, Offset);
|
|
|
|
// Debugging
|
|
ReadDebugging(false, Buffer, Offset);
|
|
}
|
|
/////////////////////
|
|
};
|
|
|
|
|
|
|
|
//******************************************************************************
|
|
// Function Definitions
|
|
//******************************************************************************
|
|
|
|
void SendAcc(u8 _ReportID)
|
|
{
|
|
byte DataAcc[MAX_PAYLOAD];
|
|
|
|
DataAcc[0] = 0x22; // Report 0x12
|
|
DataAcc[1] = 0x00; // Core buttons
|
|
DataAcc[2] = 0x00;
|
|
DataAcc[3] = _ReportID; // Reporting mode
|
|
|
|
wiiuse_io_write(WiiMoteReal::g_WiiMotesFromWiiUse[0], (byte*)DataAcc, MAX_PAYLOAD);
|
|
|
|
std::string Temp = ArrayToString(DataAcc, 28, 0, 30);
|
|
Console::Print("SendAcc: %s\n", Temp.c_str());
|
|
|
|
//22 00 00 _reportID 00
|
|
}
|
|
|
|
// Clear any potential queued events
|
|
void ClearEvents()
|
|
{
|
|
for (int i = 0; i < g_NumberOfWiiMotes; i++)
|
|
g_WiiMotes[i]->ClearEvents();
|
|
}
|
|
|
|
// Flash lights, and if connecting, also rumble
|
|
void FlashLights(bool Connect)
|
|
{
|
|
if(Connect) wiiuse_rumble(WiiMoteReal::g_WiiMotesFromWiiUse[0], 1);
|
|
wiiuse_set_leds(WiiMoteReal::g_WiiMotesFromWiiUse[0], WIIMOTE_LED_1 | WIIMOTE_LED_2 | WIIMOTE_LED_3 | WIIMOTE_LED_4);
|
|
sleep(100);
|
|
if(Connect) wiiuse_rumble(WiiMoteReal::g_WiiMotesFromWiiUse[0], 0);
|
|
|
|
// End with light 1 or 4
|
|
if(Connect)
|
|
wiiuse_set_leds(WiiMoteReal::g_WiiMotesFromWiiUse[0], WIIMOTE_LED_1);
|
|
else
|
|
wiiuse_set_leds(WiiMoteReal::g_WiiMotesFromWiiUse[0], WIIMOTE_LED_4);
|
|
}
|
|
|
|
int Initialize()
|
|
{
|
|
// Return if already initialized
|
|
if (g_RealWiiMoteInitialized) return g_NumberOfWiiMotes;
|
|
|
|
// Clear the wiimote classes
|
|
memset(g_WiiMotes, 0, sizeof(CWiiMote*) * MAX_WIIMOTES);
|
|
|
|
// Call Wiiuse.dll
|
|
g_WiiMotesFromWiiUse = wiiuse_init(MAX_WIIMOTES);
|
|
g_NumberOfWiiMotes = wiiuse_find(g_WiiMotesFromWiiUse, MAX_WIIMOTES, 5);
|
|
if (g_NumberOfWiiMotes > 0) g_RealWiiMotePresent = true;
|
|
Console::Print("Found No of Wiimotes: %i\n", g_NumberOfWiiMotes);
|
|
|
|
// Remove the wiiuse_poll() threshold
|
|
wiiuse_set_accel_threshold(g_WiiMotesFromWiiUse[0], 0);
|
|
|
|
// Set the sensor bar position, this should only affect the internal wiiuse api functions
|
|
wiiuse_set_ir_position(g_WiiMotesFromWiiUse[0], WIIUSE_IR_ABOVE);
|
|
|
|
// I don't seem to need wiiuse_connect() in Windows. But Linux needs it.
|
|
#ifndef _WIN32
|
|
int Connect = wiiuse_connect(g_WiiMotesFromWiiUse, MAX_WIIMOTES);
|
|
Console::Print("Connected: %i\n", Connect);
|
|
#endif
|
|
|
|
// If we are connecting from the config window without a game running we flash the lights
|
|
if (!g_EmulatorRunning && g_RealWiiMotePresent) FlashLights(true);
|
|
|
|
// Create Wiimote classes
|
|
for (int i = 0; i < g_NumberOfWiiMotes; i++)
|
|
g_WiiMotes[i] = new CWiiMote(i + 1, g_WiiMotesFromWiiUse[i]);
|
|
|
|
// Create a new thread and start listening for Wiimote data
|
|
if (g_NumberOfWiiMotes > 0)
|
|
g_pReadThread = new Common::Thread(ReadWiimote_ThreadFunc, NULL);
|
|
|
|
// If we are not using the emulated wiimote we can run the thread temporary until the data has beeen copied
|
|
if(g_Config.bUseRealWiimote) g_RunTemporary = true;
|
|
|
|
/* Allocate memory and copy the Wiimote eeprom accelerometer neutral values to g_Eeprom. We can't
|
|
change the neutral values the wiimote will report, I think, unless we update its eeprom? In any
|
|
case it's probably better to let the current calibration be where it is and adjust the global
|
|
values after that. I don't feel comfortable with overwriting critical data on a lot of Wiimotes. */
|
|
byte *data = (byte*)malloc(sizeof(byte) * sizeof(WiiMoteEmu::EepromData_0));
|
|
wiiuse_read_data(g_WiiMotesFromWiiUse[0], data, 0, sizeof(WiiMoteEmu::EepromData_0));
|
|
|
|
// Don't run the Wiimote thread if no wiimotes were found
|
|
if (g_NumberOfWiiMotes > 0) g_Shutdown = false;
|
|
|
|
// Initialized, even if we didn't find a Wiimote
|
|
g_RealWiiMoteInitialized = true;
|
|
|
|
return g_NumberOfWiiMotes;
|
|
}
|
|
|
|
void DoState(void* ptr, int mode) {}
|
|
|
|
void Shutdown(void)
|
|
{
|
|
// Stop the loop in the thread
|
|
g_Shutdown = true;
|
|
|
|
// Stop the thread
|
|
if (g_pReadThread != NULL)
|
|
{
|
|
g_pReadThread->WaitForDeath();
|
|
delete g_pReadThread;
|
|
g_pReadThread = NULL;
|
|
}
|
|
|
|
// Delete the wiimotes
|
|
for (int i = 0; i < g_NumberOfWiiMotes; i++)
|
|
{
|
|
delete g_WiiMotes[i];
|
|
g_WiiMotes[i] = NULL;
|
|
}
|
|
|
|
// Flash flights
|
|
if (!g_EmulatorRunning && g_RealWiiMotePresent) FlashLights(false);
|
|
|
|
// Clean up wiiuse
|
|
wiiuse_cleanup(g_WiiMotesFromWiiUse, g_NumberOfWiiMotes);
|
|
|
|
// Uninitialized
|
|
g_RealWiiMoteInitialized = false;
|
|
g_RealWiiMotePresent = false;
|
|
}
|
|
|
|
void InterruptChannel(u16 _channelID, const void* _pData, u32 _Size)
|
|
{
|
|
//Console::Print("Real InterruptChannel\n");
|
|
g_WiiMotes[0]->SendData(_channelID, (const u8*)_pData, _Size);
|
|
}
|
|
|
|
void ControlChannel(u16 _channelID, const void* _pData, u32 _Size)
|
|
{
|
|
//Console::Print("Real ControlChannel\n");
|
|
g_WiiMotes[0]->SendData(_channelID, (const u8*)_pData, _Size);
|
|
}
|
|
|
|
|
|
//////////////////////////////////
|
|
// Read the Wiimote once
|
|
// ---------------
|
|
void Update()
|
|
{
|
|
//Console::Print("Real Update\n");
|
|
for (int i = 0; i < g_NumberOfWiiMotes; i++)
|
|
{
|
|
g_WiiMotes[i]->Update();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////
|
|
/* Continuously read the Wiimote status. However, the actual sending of data occurs in Update(). If we are
|
|
not currently using the real Wiimote we allow the separate ReadWiimote() function to run. Wo don't use
|
|
them at the same time to avoid a potential collision. */
|
|
// ---------------
|
|
#ifdef _WIN32
|
|
DWORD WINAPI ReadWiimote_ThreadFunc(void* arg)
|
|
#else
|
|
void *ReadWiimote_ThreadFunc(void* arg)
|
|
#endif
|
|
{
|
|
while (!g_Shutdown)
|
|
{
|
|
// We need g_ThreadGoing to do a manual WaitForSingleObject() from the configuration window
|
|
g_ThreadGoing = true;
|
|
if(g_Config.bUseRealWiimote && !g_RunTemporary)
|
|
for (int i = 0; i < g_NumberOfWiiMotes; i++) g_WiiMotes[i]->ReadData();
|
|
else
|
|
ReadWiimote();
|
|
g_ThreadGoing = false;
|
|
}
|
|
return 0;
|
|
}
|
|
////////////////////
|
|
|
|
|
|
}; // end of namespace
|
|
|