477 lines
13 KiB
C++
477 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 "Common.h" // Common
|
|
#include "LogManager.h"
|
|
#include "StringUtil.h"
|
|
#include "Timer.h"
|
|
|
|
#define EXCLUDEMAIN_H // Avoid certain declarations in main.h
|
|
#include "EmuDefinitions.h" // Local
|
|
#include "wiimote_hid.h"
|
|
#include "main.h"
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
#include "ConfigPadDlg.h"
|
|
#include "ConfigRecordingDlg.h"
|
|
#include "ConfigBasicDlg.h"
|
|
|
|
WiimotePadConfigDialog *m_PadConfigFrame = NULL;
|
|
WiimoteRecordingConfigDialog *m_RecordingConfigFrame = NULL;
|
|
WiimoteBasicConfigDialog *m_BasicConfigFrame = NULL;
|
|
#endif
|
|
#include "Config.h"
|
|
#include "pluginspecs_wiimote.h"
|
|
#include "EmuMain.h"
|
|
#if HAVE_WIIUSE
|
|
#include "wiimote_real.h"
|
|
#endif
|
|
|
|
#if defined(HAVE_X11) && HAVE_X11
|
|
Display* WMdisplay;
|
|
#endif
|
|
SWiimoteInitialize g_WiimoteInitialize;
|
|
PLUGIN_GLOBALS* globals = NULL;
|
|
|
|
// General
|
|
bool g_EmulatorRunning = false;
|
|
u32 g_ISOId = 0;
|
|
bool g_SearchDeviceDone = false;
|
|
bool g_RealWiiMotePresent = false;
|
|
|
|
// Debugging
|
|
bool g_DebugAccelerometer = false;
|
|
bool g_DebugData = false;
|
|
bool g_DebugComm = false;
|
|
bool g_DebugSoundData = false;
|
|
bool g_DebugCustom = false;
|
|
|
|
// Update speed
|
|
int g_UpdateCounter = 0;
|
|
double g_UpdateTime = 0;
|
|
int g_UpdateRate = 0;
|
|
int g_UpdateWriteScreen = 0;
|
|
std::vector<int> g_UpdateTimeList (5, 0);
|
|
|
|
// Movement recording
|
|
std::vector<SRecordingAll> VRecording(RECORDING_ROWS);
|
|
|
|
PLUGIN_EMUSTATE g_EmulatorState = PLUGIN_EMUSTATE_STOP;
|
|
|
|
// Standard crap to make wxWidgets happy
|
|
#ifdef _WIN32
|
|
HINSTANCE g_hInstance;
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
class wxDLLApp : public wxApp
|
|
{
|
|
bool OnInit()
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_APP_NO_MAIN(wxDLLApp)
|
|
WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
|
|
#endif
|
|
|
|
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, // DLL module handle
|
|
DWORD dwReason, // reason called
|
|
LPVOID lpvReserved) // reserved
|
|
{
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
wxSetInstance((HINSTANCE)hinstDLL);
|
|
wxInitialize();
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
wxUninitialize();
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
|
|
g_hInstance = hinstDLL;
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
wxWindow* GetParentedWxWindow(HWND Parent)
|
|
{
|
|
#ifdef _WIN32
|
|
wxSetInstance((HINSTANCE)g_hInstance);
|
|
#endif
|
|
wxWindow *win = new wxWindow();
|
|
#ifdef _WIN32
|
|
win->SetHWND((WXHWND)Parent);
|
|
win->AdoptAttributesFromHWND();
|
|
#endif
|
|
return win;
|
|
}
|
|
#endif
|
|
|
|
// Exports
|
|
void GetDllInfo(PLUGIN_INFO* _PluginInfo)
|
|
{
|
|
_PluginInfo->Version = 0x0100;
|
|
_PluginInfo->Type = PLUGIN_TYPE_WIIMOTE;
|
|
#ifdef DEBUGFAST
|
|
sprintf(_PluginInfo->Name, "Dolphin Wiimote Plugin (DebugFast)");
|
|
#else
|
|
#ifndef _DEBUG
|
|
sprintf(_PluginInfo->Name, "Dolphin Wiimote Plugin");
|
|
#else
|
|
sprintf(_PluginInfo->Name, "Dolphin Wiimote Plugin (Debug)");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals)
|
|
{
|
|
globals = _pPluginGlobals;
|
|
LogManager::SetInstance((LogManager *)globals->logManager);
|
|
}
|
|
|
|
void DllDebugger(HWND _hParent, bool Show) {}
|
|
|
|
void DllConfig(HWND _hParent)
|
|
{
|
|
#ifdef _WIN32
|
|
if (WiiMoteReal::g_AutoPairUpInvisibleWindow == NULL)
|
|
{
|
|
WiiMoteReal::g_AutoPairUpInvisibleWindow = new Common::Thread(WiiMoteReal::RunInvisibleMessageWindow_ThreadFunc, NULL);
|
|
WiiMoteReal::g_AutoPairUpMonitoring = new Common::Thread(WiiMoteReal::PairUp_ThreadFunc, NULL);
|
|
}
|
|
#endif
|
|
if (!g_SearchDeviceDone)
|
|
{
|
|
// Load settings
|
|
g_Config.Load();
|
|
// We do a pad search before creating the dialog
|
|
WiiMoteEmu::Search_Devices(WiiMoteEmu::joyinfo, WiiMoteEmu::NumPads, WiiMoteEmu::NumGoodPads);
|
|
g_SearchDeviceDone = true;
|
|
}
|
|
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
wxWindow *frame = GetParentedWxWindow(_hParent);
|
|
m_BasicConfigFrame = new WiimoteBasicConfigDialog(frame);
|
|
#ifdef _WIN32
|
|
frame->Disable();
|
|
m_BasicConfigFrame->ShowModal();
|
|
frame->Enable();
|
|
#else
|
|
m_BasicConfigFrame->ShowModal();
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
frame->SetFocus();
|
|
frame->SetHWND(NULL);
|
|
#endif
|
|
|
|
m_BasicConfigFrame->Destroy();
|
|
m_BasicConfigFrame = NULL;
|
|
frame->Destroy();
|
|
#endif
|
|
}
|
|
|
|
// Start emulation
|
|
void Initialize(void *init)
|
|
{
|
|
g_EmulatorRunning = true;
|
|
g_WiimoteInitialize = *(SWiimoteInitialize *)init;
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
// Load the ISO Id
|
|
g_ISOId = g_WiimoteInitialize.ISOId;
|
|
// Load the settings
|
|
g_Config.Load();
|
|
#endif
|
|
#if defined(HAVE_X11) && HAVE_X11
|
|
WMdisplay = (Display*)g_WiimoteInitialize.hWnd;
|
|
#endif
|
|
|
|
g_ISOId = g_WiimoteInitialize.ISOId;
|
|
DEBUG_LOG(WIIMOTE, "ISOId: %08x %s", g_WiimoteInitialize.ISOId, Hex2Ascii(g_WiimoteInitialize.ISOId).c_str());
|
|
|
|
// Load IR settings, as this is a per-game setting and the user might have loaded a different game
|
|
g_Config.LoadIR();
|
|
|
|
// Run this first so that WiiMoteReal::Initialize() overwrites g_Eeprom
|
|
WiiMoteEmu::Initialize();
|
|
|
|
/* We will run WiiMoteReal::Initialize() even if we are not using a real
|
|
wiimote, to check if there is a real wiimote connected. We will initiate
|
|
wiiuse.dll, but we will return before creating a new thread for it if we
|
|
find no real Wiimotes. Then g_RealWiiMotePresent will also be
|
|
false. This function call will be done instantly whether there is a real
|
|
Wiimote connected or not. It takes no time for Wiiuse to check for
|
|
connected Wiimotes. */
|
|
#if HAVE_WIIUSE
|
|
|
|
WiiMoteReal::Initialize();
|
|
WiiMoteReal::Allocate();
|
|
#ifdef _WIN32
|
|
if (WiiMoteReal::g_AutoPairUpInvisibleWindow == NULL)
|
|
{
|
|
WiiMoteReal::g_AutoPairUpInvisibleWindow = new Common::Thread(WiiMoteReal::RunInvisibleMessageWindow_ThreadFunc, NULL);
|
|
WiiMoteReal::g_AutoPairUpMonitoring = new Common::Thread(WiiMoteReal::PairUp_ThreadFunc, NULL);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
// If a game is not running this is called by the Configuration window when it's closed
|
|
void Shutdown(void)
|
|
{
|
|
// Not running
|
|
g_EmulatorRunning = false;
|
|
|
|
// Reset the game ID in all cases
|
|
g_ISOId = 0;
|
|
|
|
#if HAVE_WIIUSE
|
|
WiiMoteReal::Shutdown();
|
|
#endif
|
|
WiiMoteEmu::Shutdown();
|
|
}
|
|
|
|
|
|
void DoState(unsigned char **ptr, int mode)
|
|
{
|
|
PointerWrap p(ptr, mode);
|
|
|
|
// TODO: Shorten the list
|
|
|
|
//p.Do(g_EmulatorRunning);
|
|
//p.Do(g_ISOId);
|
|
//p.Do(g_RealWiiMotePresent);
|
|
//p.Do(g_RealWiiMoteInitialized);
|
|
//p.Do(g_EmulatedWiiMoteInitialized);
|
|
//p.Do(g_UpdateCounter);
|
|
//p.Do(g_UpdateTime);
|
|
//p.Do(g_UpdateRate);
|
|
//p.Do(g_UpdateWriteScreen);
|
|
//p.Do(g_UpdateTimeList);
|
|
|
|
#if HAVE_WIIUSE
|
|
WiiMoteReal::DoState(p);
|
|
#endif
|
|
WiiMoteEmu::DoState(p);
|
|
|
|
return;
|
|
}
|
|
void EmuStateChange(PLUGIN_EMUSTATE newState)
|
|
{
|
|
g_EmulatorState = newState;
|
|
}
|
|
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
// Hack to use wx key events
|
|
volatile bool wxkeystate[WXK_SPECIAL20];
|
|
#endif
|
|
|
|
// Set buttons status from keyboard input. Currently this is done from
|
|
// wxWidgets in the main application.
|
|
// --------------
|
|
void Wiimote_Input(u16 _Key, u8 _UpDown)
|
|
{
|
|
#if defined(__APPLE__) && defined(USE_WX) && USE_WX
|
|
if (_Key < WXK_SPECIAL20)
|
|
{
|
|
wxkeystate[_Key] = _UpDown;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* This function produce Wiimote Input (reports from the Wiimote) in response
|
|
to Output from the Wii. It's called from WII_IPC_HLE_WiiMote.cpp.
|
|
|
|
Switch between real and emulated wiimote: We send all this Input to WiiMoteEmu::InterruptChannel()
|
|
so that it knows the channel ID and the data reporting mode at all times.
|
|
*/
|
|
void Wiimote_InterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
|
|
{
|
|
// Decide where to send the message
|
|
if (WiiMoteEmu::WiiMapping[_number].Source <= 1)
|
|
WiiMoteEmu::InterruptChannel(_number, _channelID, _pData, _Size);
|
|
#if HAVE_WIIUSE
|
|
else if (g_RealWiiMotePresent)
|
|
WiiMoteReal::InterruptChannel(_number, _channelID, _pData, _Size);
|
|
#endif
|
|
}
|
|
|
|
|
|
// Function: Used for the initial Bluetooth HID handshake.
|
|
void Wiimote_ControlChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
|
|
{
|
|
// Debugging
|
|
#if defined(_DEBUG) || defined(DEBUGFAST)
|
|
DEBUG_LOG(WIIMOTE, "Wiimote_ControlChannel");
|
|
DEBUG_LOG(WIIMOTE, " Channel ID: %04x", _channelID);
|
|
std::string Temp = ArrayToString((const u8*)_pData, _Size);
|
|
DEBUG_LOG(WIIMOTE, " Data: %s", Temp.c_str());
|
|
#endif
|
|
|
|
// Check for custom communication
|
|
if(_channelID == 99 && *(const u8*)_pData == WIIMOTE_DISCONNECT)
|
|
{
|
|
WiiMoteEmu::g_ReportingAuto[_number] = false;
|
|
WARN_LOG(WIIMOTE, "Wiimote: #%i Disconnected", _number);
|
|
#ifdef _WIN32
|
|
PostMessage(g_WiimoteInitialize.hWnd, WM_USER, WIIMOTE_DISCONNECT, _number);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (WiiMoteEmu::WiiMapping[_number].Source <= 1)
|
|
WiiMoteEmu::ControlChannel(_number, _channelID, _pData, _Size);
|
|
#if HAVE_WIIUSE
|
|
else if (g_RealWiiMotePresent)
|
|
WiiMoteReal::ControlChannel(_number, _channelID, _pData, _Size);
|
|
#endif
|
|
}
|
|
|
|
|
|
// This sends a Data Report from the Wiimote. See SystemTimers.cpp for the documentation of this update.
|
|
void Wiimote_Update(int _number)
|
|
{
|
|
// Tell us about the update rate, but only about once every second to avoid a major slowdown
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
if (m_RecordingConfigFrame)
|
|
{
|
|
GetUpdateRate();
|
|
if (g_UpdateWriteScreen > g_UpdateRate)
|
|
{
|
|
m_RecordingConfigFrame->m_TextUpdateRate->SetLabel(wxString::Format(wxT("Update rate: %03i times/s"), g_UpdateRate));
|
|
g_UpdateWriteScreen = 0;
|
|
}
|
|
g_UpdateWriteScreen++;
|
|
}
|
|
#endif
|
|
|
|
// This functions will send:
|
|
// Emulated Wiimote: Only data reports 0x30-0x37
|
|
// Real Wiimote: Both data reports 0x30-0x37 and all other read reports
|
|
if (WiiMoteEmu::WiiMapping[_number].Source <= 1)
|
|
WiiMoteEmu::Update(_number);
|
|
#if HAVE_WIIUSE
|
|
else if (g_RealWiiMotePresent)
|
|
WiiMoteReal::Update(_number);
|
|
#endif
|
|
|
|
/*
|
|
// Debugging
|
|
#ifdef _WIN32
|
|
if( GetAsyncKeyState(VK_HOME) && g_DebugComm ) g_DebugComm = false; // Page Down
|
|
else if (GetAsyncKeyState(VK_HOME) && !g_DebugComm ) g_DebugComm = true;
|
|
|
|
if( GetAsyncKeyState(VK_PRIOR) && g_DebugData ) g_DebugData = false; // Page Up
|
|
else if (GetAsyncKeyState(VK_PRIOR) && !g_DebugData ) g_DebugData = true;
|
|
|
|
if( GetAsyncKeyState(VK_NEXT) && g_DebugAccelerometer ) g_DebugAccelerometer = false; // Home
|
|
else if (GetAsyncKeyState(VK_NEXT) && !g_DebugAccelerometer ) g_DebugAccelerometer = true;
|
|
|
|
if( GetAsyncKeyState(VK_END) && g_DebugCustom ) { g_DebugCustom = false; DEBUG_LOG(WIIMOTE, "Custom Debug: Off");} // End
|
|
else if (GetAsyncKeyState(VK_END) && !g_DebugCustom ) {g_DebugCustom = true; DEBUG_LOG(WIIMOTE, "Custom Debug: Off");}
|
|
#endif
|
|
*/
|
|
|
|
}
|
|
|
|
unsigned int Wiimote_GetAttachedControllers()
|
|
{
|
|
unsigned int attached = 0;
|
|
for (unsigned int i=0; i<4; ++i)
|
|
if (WiiMoteEmu::WiiMapping[i].Source)
|
|
attached |= (1 << i);
|
|
return attached;
|
|
}
|
|
|
|
|
|
// Unpair Wiimotes, TODO: Add linux/osx un-pair function
|
|
unsigned int Wiimote_UnPairWiimotes()
|
|
{
|
|
#ifdef _WIN32
|
|
if (g_Config.bUnpairRealWiimote)
|
|
return WiiMoteReal::WiimotePairUp(true);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
// Supporting functions
|
|
|
|
// Check if the render window is in focus
|
|
|
|
bool IsFocus()
|
|
{
|
|
#if defined(__APPLE__) && defined(USE_WX) && USE_WX
|
|
return true; /* XXX */
|
|
#endif
|
|
return g_WiimoteInitialize.pRendererHasFocus();
|
|
}
|
|
|
|
/* Returns a timestamp with three decimals for precise time comparisons. The return format is
|
|
of the form seconds.milleseconds for example 1234.123. The leding seconds have no particular meaning
|
|
but are just there to enable use to tell if we have entered a new second or now. */
|
|
// -----------------
|
|
|
|
/* Calculate the current update frequency. Calculate the time between ten updates, and average
|
|
five such rates. If we assume there are 60 updates per second if the game is running at full
|
|
speed then we get this measure on average once every second. The reason to have a few updates
|
|
between each measurement is becase the milliseconds may not be perfectly accurate and may return
|
|
the same time even when a milliseconds has actually passed, for example.*/
|
|
int GetUpdateRate()
|
|
{
|
|
#if defined(HAVE_WX) && HAVE_WX
|
|
if(g_UpdateCounter == 10)
|
|
{
|
|
// Erase the old ones
|
|
if(g_UpdateTimeList.size() == 5) g_UpdateTimeList.erase(g_UpdateTimeList.begin() + 0);
|
|
|
|
// Calculate the time and save it
|
|
int Time = (int)(10 / (Common::Timer::GetDoubleTime() - g_UpdateTime));
|
|
g_UpdateTimeList.push_back(Time);
|
|
//DEBUG_LOG(WIIMOTE, "Time: %i %f", Time, Common::Timer::GetDoubleTime());
|
|
|
|
int TotalTime = 0;
|
|
for (int i = 0; i < (int)g_UpdateTimeList.size(); i++)
|
|
TotalTime += g_UpdateTimeList.at(i);
|
|
g_UpdateRate = TotalTime / 5;
|
|
|
|
// Write the new update time
|
|
g_UpdateTime = Common::Timer::GetDoubleTime();
|
|
|
|
g_UpdateCounter = 0;
|
|
}
|
|
|
|
g_UpdateCounter++;
|
|
|
|
return g_UpdateRate;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|