mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 22:09:19 -07:00
664cea45c7
Using Unix tools to operate on a tree containing filename with spaces in them is really annoying, so rename the handful of instances where there were spaces. Host.cpp has never been used. Games tend to lookup the following directories that we don't yet have anything to put in, so prepopulate them in Data/User/Wii: title/00010001 title/00010002 title/00010003 title/00010004 title/00010005 title/00010006 title/00010007 meta shared2/title Set eol-style native on a number of text files which didn't already have it. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5572 8ced0084-cf51-0410-be5f-012b33b47a6e
406 lines
12 KiB
C++
406 lines
12 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 "EmuDefinitions.h"
|
||
#ifdef _WIN32
|
||
#include "XInput.h"
|
||
#endif
|
||
|
||
namespace WiiMoteEmu
|
||
{
|
||
|
||
// SDL Haptic fails on windows, it just doesn't work (even the sample doesn't work)
|
||
// So until i can make it work, this is all disabled >:(
|
||
#if SDL_VERSION_ATLEAST(1, 3, 0) && !defined(_WIN32) && !defined(__APPLE__)
|
||
#define SDL_RUMBLE
|
||
#else
|
||
#ifdef _WIN32
|
||
#define RUMBLE_HACK
|
||
#define DIRECTINPUT_VERSION 0x0800
|
||
#define WIN32_LEAN_AND_MEAN
|
||
|
||
#pragma comment(lib, "dxguid.lib")
|
||
#pragma comment(lib, "dinput8.lib")
|
||
#pragma comment(lib, "winmm.lib")
|
||
#include <dinput.h>
|
||
#endif
|
||
#endif
|
||
|
||
|
||
#ifdef RUMBLE_HACK
|
||
|
||
struct RUMBLE // GC Pad rumble DIDevice
|
||
{
|
||
LPDIRECTINPUTDEVICE8 g_pDevice; // 4 pads objects
|
||
LPDIRECTINPUTEFFECT g_pEffect;
|
||
DWORD g_dwNumForceFeedbackAxis;
|
||
DIEFFECT eff;
|
||
};
|
||
|
||
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
|
||
|
||
BOOL CALLBACK EnumFFDevicesCallback(const DIDEVICEINSTANCE* pInst, VOID* pContext);
|
||
BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext);
|
||
void SetDeviceForcesXY(int pad, int nXYForce);
|
||
HRESULT InitRumble(HWND hWnd);
|
||
void Rumble_DInput(int _ID, unsigned int _Strength);
|
||
void Rumble_XInput(int _ID, unsigned int _Strength);
|
||
|
||
|
||
LPDIRECTINPUT8 g_Rumble; // DInput Rumble object
|
||
RUMBLE pRumble[MAX_WIIMOTES];
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////
|
||
// Set PAD rumble. Explanation: Stop = 0, Rumble = 1
|
||
void PAD_Rumble(u8 _numPAD, unsigned int _uType)
|
||
{
|
||
if (WiiMapping[_numPAD].ID >= NumPads || !WiiMapping[_numPAD].Rumble)
|
||
return;
|
||
|
||
unsigned int Strength = 0;
|
||
if (_uType == 1)
|
||
{
|
||
Strength = WiiMapping[_numPAD].RumbleStrength;
|
||
Strength = Strength > 100 ? 100 : Strength;
|
||
}
|
||
|
||
if (WiiMapping[_numPAD].TriggerType == InputCommon::CTL_TRIGGER_XINPUT)
|
||
Rumble_XInput(WiiMapping[_numPAD].ID, Strength);
|
||
else
|
||
Rumble_DInput(WiiMapping[_numPAD].ID, Strength);
|
||
}
|
||
|
||
////////////////////////////////////////////////////
|
||
// Set rumble with XInput.
|
||
void Rumble_XInput(int _ID, unsigned int _Strength)
|
||
{
|
||
#ifdef _WIN32
|
||
XINPUT_VIBRATION vib;
|
||
vib.wLeftMotorSpeed = 0xFFFF / 100 * _Strength;
|
||
vib.wRightMotorSpeed = 0xFFFF / 100 * _Strength;
|
||
XInputSetState(_ID, &vib);
|
||
#endif
|
||
}
|
||
|
||
////////////////////////////////////////////////////
|
||
// Set rumble with DInput.<2E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
void Rumble_DInput(int _ID, unsigned int _Strength)
|
||
{
|
||
if (!g_Rumble)
|
||
{
|
||
// GetForegroundWindow() always sends the good HWND
|
||
if (FAILED(InitRumble(GetForegroundWindow())))
|
||
PanicAlert("Could not initialize Rumble!");
|
||
}
|
||
else
|
||
{
|
||
// Acquire gamepad
|
||
if (pRumble[_ID].g_pDevice != NULL)
|
||
pRumble[_ID].g_pDevice->Acquire();
|
||
}
|
||
|
||
SetDeviceForcesXY(_ID, _Strength * 100);
|
||
}
|
||
|
||
HRESULT InitRumble(HWND hWnd)
|
||
{
|
||
DIPROPDWORD dipdw;
|
||
HRESULT hr;
|
||
|
||
// Register with the DirectInput subsystem and get a pointer to a IDirectInput interface we can use.
|
||
if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&g_Rumble, NULL)))
|
||
return hr;
|
||
|
||
// Look for a device we can use
|
||
if (FAILED(hr = g_Rumble->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumFFDevicesCallback, NULL, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK)))
|
||
return hr;
|
||
|
||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||
{
|
||
if (NULL == pRumble[i].g_pDevice)
|
||
WiiMapping[i].Rumble = false; // Disable Rumble for this pad only.
|
||
else
|
||
{
|
||
pRumble[i].g_pDevice->SetDataFormat(&c_dfDIJoystick);
|
||
pRumble[i].g_pDevice->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND);
|
||
// Request exclusive acces for both background and foreground.
|
||
|
||
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||
dipdw.diph.dwObj = 0;
|
||
dipdw.diph.dwHow = DIPH_DEVICE;
|
||
dipdw.dwData = FALSE;
|
||
|
||
// if Force Feedback doesn't seem to work...
|
||
if (FAILED(pRumble[i].g_pDevice->EnumObjects(EnumAxesCallback,
|
||
(void*)&pRumble[i].g_dwNumForceFeedbackAxis, DIDFT_AXIS))
|
||
|| FAILED(pRumble[i].g_pDevice->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph)))
|
||
{
|
||
PanicAlert("Device %d doesn't seem to work ! \nRumble for device %d is now Disabled !", i+1);
|
||
|
||
WiiMapping[i].Rumble = false; // Disable Rumble for this pad
|
||
|
||
continue; // Next pad
|
||
}
|
||
|
||
if (pRumble[i].g_dwNumForceFeedbackAxis > 2)
|
||
pRumble[i].g_dwNumForceFeedbackAxis = 2;
|
||
|
||
DWORD _rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y};
|
||
long rglDirection[2] = {0, 0};
|
||
DICONSTANTFORCE cf = {0};
|
||
|
||
ZeroMemory(&pRumble[i].eff, sizeof(pRumble[i].eff));
|
||
pRumble[i].eff.dwSize = sizeof(DIEFFECT);
|
||
pRumble[i].eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
|
||
pRumble[i].eff.dwDuration = INFINITE; // fixed time may be safer (X * DI_SECONDS)
|
||
pRumble[i].eff.dwSamplePeriod = 0;
|
||
pRumble[i].eff.dwGain = DI_FFNOMINALMAX;
|
||
pRumble[i].eff.dwTriggerButton = DIEB_NOTRIGGER;
|
||
pRumble[i].eff.dwTriggerRepeatInterval = 0;
|
||
pRumble[i].eff.cAxes = pRumble[i].g_dwNumForceFeedbackAxis;
|
||
pRumble[i].eff.rgdwAxes = _rgdwAxes;
|
||
pRumble[i].eff.rglDirection = rglDirection;
|
||
pRumble[i].eff.lpEnvelope = 0;
|
||
pRumble[i].eff.cbTypeSpecificParams = sizeof( DICONSTANTFORCE );
|
||
pRumble[i].eff.lpvTypeSpecificParams = &cf;
|
||
pRumble[i].eff.dwStartDelay = 0;
|
||
|
||
// Create the prepared effect
|
||
if (FAILED(hr = pRumble[i].g_pDevice->CreateEffect(GUID_ConstantForce, &pRumble[i].eff, &pRumble[i].g_pEffect, NULL)))
|
||
continue;
|
||
|
||
if (pRumble[i].g_pEffect == NULL)
|
||
continue;
|
||
}
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
void SetDeviceForcesXY(int npad, int nXYForce)
|
||
{
|
||
// Security check
|
||
if (pRumble[npad].g_pDevice == NULL)
|
||
return;
|
||
|
||
// If nXYForce is null, there's no point to create the effect
|
||
// Just stop the force feedback
|
||
if (nXYForce == 0) {
|
||
pRumble[npad].g_pEffect->Stop();
|
||
return;
|
||
}
|
||
|
||
long rglDirection[2] = {0};
|
||
DICONSTANTFORCE cf;
|
||
|
||
// If only one force feedback axis, then apply only one direction and keep the direction at zero
|
||
if (pRumble[npad].g_dwNumForceFeedbackAxis == 1)
|
||
{
|
||
rglDirection[0] = 0;
|
||
cf.lMagnitude = nXYForce; // max should be 10000
|
||
}
|
||
// If two force feedback axis, then apply magnitude from both directions
|
||
else
|
||
{
|
||
rglDirection[0] = nXYForce;
|
||
rglDirection[1] = nXYForce;
|
||
cf.lMagnitude = static_cast<LONG>(1.4142f*nXYForce);
|
||
}
|
||
|
||
ZeroMemory(&pRumble[npad].eff, sizeof(pRumble[npad].eff));
|
||
pRumble[npad].eff.dwSize = sizeof(DIEFFECT);
|
||
pRumble[npad].eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
|
||
pRumble[npad].eff.cAxes = pRumble[npad].g_dwNumForceFeedbackAxis;
|
||
pRumble[npad].eff.rglDirection = rglDirection;
|
||
pRumble[npad].eff.lpEnvelope = 0;
|
||
pRumble[npad].eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
|
||
pRumble[npad].eff.lpvTypeSpecificParams = &cf;
|
||
pRumble[npad].eff.dwStartDelay = 0;
|
||
|
||
// Now set the new parameters..
|
||
pRumble[npad].g_pEffect->SetParameters(&pRumble[npad].eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START);
|
||
// ..And start the effect immediately.
|
||
if (pRumble[npad].g_pEffect != NULL)
|
||
pRumble[npad].g_pEffect->Start(1, 0);
|
||
}
|
||
|
||
BOOL CALLBACK EnumFFDevicesCallback(const DIDEVICEINSTANCE* pInst, VOID* pContext)
|
||
{
|
||
LPDIRECTINPUTDEVICE8 pDevice;
|
||
DIPROPDWORD dipdw;
|
||
HRESULT hr;
|
||
|
||
int JoystickID;
|
||
|
||
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
||
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||
dipdw.diph.dwObj = 0;
|
||
dipdw.diph.dwHow = DIPH_DEVICE;
|
||
|
||
g_Rumble->CreateDevice(pInst->guidInstance, &pDevice, NULL); // Create a DInput pad device
|
||
|
||
if (SUCCEEDED(hr = pDevice->GetProperty(DIPROP_JOYSTICKID, &dipdw.diph))) // Get DInput Device ID
|
||
JoystickID = dipdw.dwData;
|
||
else
|
||
return DIENUM_CONTINUE;
|
||
|
||
//PanicAlert("DInput ID : %d \nSDL ID (1-4) : %d / %d / %d / %d\n", JoystickID, WiiMapping[0].ID, WiiMapping[1].ID, WiiMapping[2].ID, WiiMapping[3].ID);
|
||
|
||
for (int i=0; i<4; i++)
|
||
{
|
||
if (WiiMapping[i].ID == JoystickID) // if SDL ID = DInput ID -> we're dealing with the same device
|
||
{
|
||
// a DInput device is created even if rumble is disabled on startup
|
||
// this way, you can toggle the rumble setting while in game
|
||
pRumble[i].g_pDevice = pDevice; // everything looks good, save the DInput device
|
||
}
|
||
}
|
||
|
||
return DIENUM_CONTINUE;
|
||
}
|
||
|
||
BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext)
|
||
{
|
||
DWORD* pdwNumForceFeedbackAxis = (DWORD*)pContext; // Enum Rumble Axis
|
||
if ((pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0)
|
||
(*pdwNumForceFeedbackAxis)++;
|
||
|
||
return DIENUM_CONTINUE;
|
||
}
|
||
|
||
void PAD_RumbleClose()
|
||
{
|
||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
||
{
|
||
if (WiiMapping[i].ID < NumPads)
|
||
if (WiiMapping[i].TriggerType == InputCommon::CTL_TRIGGER_XINPUT)
|
||
{
|
||
#ifdef _WIN32
|
||
// Kill Xpad rumble
|
||
XINPUT_VIBRATION vib;
|
||
vib.wLeftMotorSpeed = 0;
|
||
vib.wRightMotorSpeed = 0;
|
||
XInputSetState(WiiMapping[i].ID, &vib);
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
// It may look weird, but we don't free anything here, it was the cause of crashes
|
||
// on stop, and the DLL isn't unloaded anyway, so the pointers stay
|
||
// We just stop the rumble in case it's still playing an effect.
|
||
if (pRumble[WiiMapping[i].ID].g_pDevice && pRumble[WiiMapping[i].ID].g_pEffect)
|
||
pRumble[WiiMapping[i].ID].g_pEffect->Stop();
|
||
}
|
||
}
|
||
}
|
||
|
||
#else // Multiplatform SDL Rumble code
|
||
|
||
#ifdef SDL_RUMBLE
|
||
|
||
struct RUMBLE // GC Pad rumble DIDevice
|
||
{
|
||
SDL_Haptic* g_pDevice;
|
||
SDL_HapticEffect g_pEffect;
|
||
int effect_id;
|
||
};
|
||
|
||
RUMBLE pRumble[4] = {0}; // 4 GC Rumble Pads
|
||
#endif
|
||
|
||
|
||
// Use PAD rumble
|
||
// --------------
|
||
bool PAD_Init_Rumble(u8 _numPAD, SDL_Joystick *SDL_Device)
|
||
{
|
||
#ifdef SDL_RUMBLE
|
||
if (SDL_Device == NULL)
|
||
return false;
|
||
|
||
pRumble[_numPAD].g_pDevice = SDL_HapticOpenFromJoystick(SDL_Device);
|
||
|
||
if (pRumble[_numPAD].g_pDevice == NULL)
|
||
return false; // Most likely joystick isn't haptic
|
||
|
||
if (!(SDL_HapticQuery(pRumble[_numPAD].g_pDevice) & SDL_HAPTIC_CONSTANT))
|
||
{
|
||
SDL_HapticClose(pRumble[_numPAD].g_pDevice); // No effect
|
||
pRumble[_numPAD].g_pDevice = 0;
|
||
WiiMapping[_numPAD].Rumble = false;
|
||
return false;
|
||
}
|
||
|
||
// Set the strength of the rumble effect
|
||
int Strenght = 3276 * (pRumble[_numPAD].RumbleStrength);
|
||
Strenght = Strenght > 32767 ? 32767 : Strenght;
|
||
|
||
// Create the effect
|
||
memset(&pRumble[_numPAD].g_pEffect, 0, sizeof(SDL_HapticEffect)); // 0 is safe default
|
||
pRumble[_numPAD].g_pEffect.type = SDL_HAPTIC_CONSTANT;
|
||
pRumble[_numPAD].g_pEffect.constant.direction.type = SDL_HAPTIC_POLAR; // Polar coordinates
|
||
pRumble[_numPAD].g_pEffect.constant.direction.dir[0] = 18000; // Force comes from south
|
||
pRumble[_numPAD].g_pEffect.constant.level = Strenght;
|
||
pRumble[_numPAD].g_pEffect.constant.length = 10000; // 10s long (should be INFINITE, but 10s is safer)
|
||
pRumble[_numPAD].g_pEffect.constant.attack_length = 0; // disable Fade in...
|
||
pRumble[_numPAD].g_pEffect.constant.fade_length = 0; // ...and out
|
||
|
||
// Upload the effect
|
||
pRumble[_numPAD].effect_id = SDL_HapticNewEffect( pRumble[_numPAD].g_pDevice, &pRumble[_numPAD].g_pEffect );
|
||
#endif
|
||
return true;
|
||
}
|
||
|
||
|
||
// Set PAD rumble. Explanation: Stop = 0, Rumble = 1
|
||
// --------------
|
||
void PAD_Rumble(u8 _numPAD, unsigned int _uType)
|
||
{
|
||
#ifdef SDL_RUMBLE
|
||
if (WiiMapping[_numPAD].Rumble) // rumble activated
|
||
{
|
||
if (!pRumble[_numPAD].g_pDevice)
|
||
return;
|
||
|
||
if (_uType == 1)
|
||
SDL_HapticRunEffect( pRumble[_numPAD].g_pDevice, pRumble[_numPAD].effect_id, 1 );
|
||
else
|
||
SDL_HapticStopAll(pRumble[_numPAD].g_pDevice);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void PAD_RumbleClose()
|
||
{
|
||
#ifdef SDL_RUMBLE
|
||
for (int i=0; i<4; i++) // Free all pads
|
||
{
|
||
if (pRumble[i].g_pDevice) {
|
||
SDL_HapticClose( pRumble[i].g_pDevice );
|
||
pRumble[i].g_pDevice = NULL;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
#endif // RUMBLE_HACK
|
||
|
||
} // end of namespace
|