Updated rumble for nJoy, fixed a few glitches with half press button and deadzones too.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2840 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
sl1nk3.s 2009-04-02 21:13:58 +00:00
parent e24e2db1a1
commit b0852c1bef
10 changed files with 365 additions and 334 deletions

View File

@ -92,6 +92,7 @@ struct CONTROLLER_MAPPING // GC PAD MAPPING
int triggertype; // Triggers range
std::string SDiagonal;
bool bSquareToCircle;
bool rumble;
int eventnum; // Linux Event Number, Can't be found dynamically yet
};

View File

@ -271,13 +271,15 @@ void DInput_Read(int _numPAD, SPADStatus* _pPADStatus)
if (dinput.diks[pad[_numPAD].keyForControl[CTL_L]] & 0xFF)
{
_pPADStatus->button |= PAD_TRIGGER_L;
if (triggervalue > 230)
_pPADStatus->button |= PAD_TRIGGER_L;
_pPADStatus->triggerLeft = triggervalue;
}
if (dinput.diks[pad[_numPAD].keyForControl[CTL_R]] & 0xFF)
{
_pPADStatus->button |= PAD_TRIGGER_R;
if (triggervalue > 230)
_pPADStatus->button |= PAD_TRIGGER_R;
_pPADStatus->triggerRight = triggervalue;
}

View File

@ -111,6 +111,7 @@ void Config::Save(int Slot)
file.Set("General", "SaveByID", g_Config.bSaveByID);
file.Set("General", "CheckForFocus", g_Config.bCheckFocus);
file.Set("General", "NoTriggerFilter", g_Config.bNoTriggerFilter);
file.Set("General", "RumbleStrength", g_Config.RumbleStrength);
#ifdef RERECORDING
file.Set("General", "Recording", g_Config.bRecording);
file.Set("General", "Playback", g_Config.bPlayback);
@ -171,6 +172,7 @@ void Config::Save(int Slot)
file.Set(SectionName.c_str(), "controllertype", PadMapping[i].controllertype);
file.Set(SectionName.c_str(), "TriggerType", PadMapping[i].triggertype);
file.Set(SectionName.c_str(), "eventnum", PadMapping[i].eventnum);
file.Set(SectionName.c_str(), "use_rumble", PadMapping[i].rumble);
file.Set(SectionName.c_str(), "Diagonal", PadMapping[i].SDiagonal);
file.Set(SectionName.c_str(), "SquareToCircle", PadMapping[i].bSquareToCircle);
@ -203,6 +205,7 @@ void Config::Load(bool ChangePad, bool ChangeSaveByID)
file.Get("General", "ShowAdvanced", &g_Config.bShowAdvanced, false);
file.Get("General", "CheckForFocus", &g_Config.bCheckFocus, false);
file.Get("General", "NoTriggerFilter", &g_Config.bNoTriggerFilter, false);
file.Get("General", "RumbleStrength", &g_Config.RumbleStrength, 8);
#ifdef RERECORDING
file.Get("General", "Recording", &g_Config.bRecording, false);
file.Get("General", "Playback", &g_Config.bPlayback, false);
@ -263,6 +266,7 @@ void Config::Load(bool ChangePad, bool ChangeSaveByID)
file.Get(SectionName.c_str(), "controllertype", &PadMapping[i].controllertype, 0);
file.Get(SectionName.c_str(), "TriggerType", &PadMapping[i].triggertype, 0);
file.Get(SectionName.c_str(), "eventnum", &PadMapping[i].eventnum, 0);
file.Get(SectionName.c_str(), "use_rumble", &PadMapping[i].rumble, false);
file.Get(SectionName.c_str(), "Diagonal", &PadMapping[i].SDiagonal, "100%");
file.Get(SectionName.c_str(), "SquareToCircle", &Tmp, false); PadMapping[i].bSquareToCircle = Tmp;

View File

@ -30,6 +30,7 @@ struct Config
bool bSaveByID;
bool bCheckFocus;
bool bNoTriggerFilter;
int RumbleStrength;
#ifdef RERECORDING
bool bRecording;
bool bPlayback;

View File

@ -6,7 +6,7 @@
//
// Author: Falcon4ever (nJoy@falcon4ever.com)
// Site: www.multigesture.net
// Copyright (C) 2003-2008 Dolphin Project.
// Copyright (C) 2003-2009 Dolphin Project.
//
//////////////////////////////////////////////////////////////////////////////////////////
//
@ -80,6 +80,10 @@ BEGIN_EVENT_TABLE(ConfigBox,wxDialog)
EVT_COMBOBOX(IDC_TRIGGERTYPE, ConfigBox::ChangeSettings)
EVT_COMBOBOX(IDC_DEADZONE, ConfigBox::ChangeSettings)
// Rumble settings
EVT_CHECKBOX(IDC_ENABLERUMBLE, ConfigBox::ChangeSettings)
EVT_COMBOBOX(IDC_RUMBLESTRENGTH, ConfigBox::ChangeSettings)
// Advanced settings
EVT_COMBOBOX(IDCB_MAINSTICK_DIAGONAL, ConfigBox::ChangeSettings)
EVT_CHECKBOX(IDCB_MAINSTICK_S_TO_C, ConfigBox::ChangeSettings)
@ -203,7 +207,6 @@ void ConfigBox::OKClick(wxCommandEvent& event)
if (event.GetId() == ID_OK)
{
DoSave(); // Save settings
//g_Config.Load(); // Reload settings to PadMapping
if(Debugging) PanicAlert("Done");
Close(); // Call OnClose()
}
@ -217,7 +220,6 @@ void ConfigBox::CancelClick(wxCommandEvent& event)
{
// Forget all potential changes to PadMapping by loading the last saved settings
g_Config.Load();
Close(); // Call OnClose()
}
}
@ -485,7 +487,13 @@ void ConfigBox::ChangeSettings( wxCommandEvent& event )
UpdateGUI(notebookpage);
}
break;
case IDC_ENABLERUMBLE:
PadMapping[notebookpage].rumble = m_Rumble[notebookpage]->IsChecked();
UpdateGUI(notebookpage);
break;
case IDC_RUMBLESTRENGTH:
g_Config.RumbleStrength = m_RStrength[notebookpage]->GetSelection();
break;
case IDC_JOYNAME:
DoChangeJoystick();
break;
@ -555,17 +563,17 @@ void ConfigBox::UpdateGUI(int _notebookpage)
m_CBShowAdvanced[_notebookpage]->SetValue(g_Config.bShowAdvanced);
m_CBCheckFocus[_notebookpage]->SetValue(g_Config.bCheckFocus);
m_AdvancedMapFilter[_notebookpage]->SetValue(g_Config.bNoTriggerFilter);
m_RStrength[_notebookpage]->SetSelection(g_Config.RumbleStrength);
#ifdef RERECORDING
m_CheckRecording[_notebookpage]->SetValue(g_Config.bRecording);
m_CheckPlayback[_notebookpage]->SetValue(g_Config.bPlayback);
#endif
//LogMsg("Update: %i\n", g_Config.bSaveByID);
// Disabled pages
bool Enabled = PadMapping[_notebookpage].enabled == 1 ? true : false;
// There is no FindItem in linux so this doesn't work
#ifdef _WIN32
// Disabled pages
bool Enabled = PadMapping[_notebookpage].enabled == 1 ? true : false;
// Enable or disable all buttons
for(int i = IDB_ANALOG_MAIN_X; i <= IDB_BUTTONHALFPRESS; i++)
m_Controller[_notebookpage]->FindItem(i)->Enable(Enabled);
@ -680,11 +688,20 @@ void ConfigBox::CreateGUIControls()
wxArrayString wxAS_TriggerType;
wxAS_TriggerType.Add(wxString::FromAscii(TriggerType[InputCommon::CTL_TRIGGER_SDL]));
wxAS_TriggerType.Add(wxString::FromAscii(TriggerType[InputCommon::CTL_TRIGGER_XINPUT]));
// --------------------------------------------------------------------
// Populate the deadzone list
// Populate the deadzone list and the Rumble Strength
// -----------------------------
char buffer [8];
char buffer[8];
wxArrayString wxAS_RumbleStrength;
for (int i = 1; i < 11; i++)
{
sprintf (buffer, "%d %%", i*10);
wxAS_RumbleStrength.Add(wxString::FromAscii(buffer));
}
wxArrayString arrayStringFor_Deadzone;
for(int x = 1; x <= 100; x++)
{
@ -692,7 +709,6 @@ void ConfigBox::CreateGUIControls()
arrayStringFor_Deadzone.Add(wxString::FromAscii(buffer));
}
// Populate all four pages
for(int i = 0; i < 4; i++)
{
@ -880,6 +896,17 @@ void ConfigBox::CreateGUIControls()
m_gGenSettingsID[i]->Add(m_CBSaveByID[i], 0, wxEXPAND | wxALL, 3);
m_gGenSettingsID[i]->Add(m_CBShowAdvanced[i], 0, wxEXPAND | wxALL, 3);
// Create objects for Rumble settings (general 4)
m_RStrength[i] = new wxComboBox(m_Controller[i], IDC_RUMBLESTRENGTH, wxAS_RumbleStrength[0], wxDefaultPosition, wxSize(85, 20), wxAS_RumbleStrength, wxCB_READONLY);
m_Rumble[i] = new wxCheckBox(m_Controller[i], IDC_ENABLERUMBLE, wxT("Enable Rumble"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator);
// Populate general settings 4
m_gRumble[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Rumble Settings"));
m_gGBRumble[i] = new wxGridBagSizer(0, 0);
m_gGBRumble[i]->Add(m_Rumble[i], wxGBPosition(0, 0), wxGBSpan(1, 1), (wxTOP), 1);
m_gGBRumble[i]->Add(m_RStrength[i], wxGBPosition(1, 0), wxGBSpan(1, 1), (wxTOP), 6);
m_gRumble[i]->Add(m_gGBRumble[i], 0, wxEXPAND | wxALL, 3);
// Create tooltips
m_ControlType[i]->SetToolTip(wxT(
"Use a 'hat' on your gamepad or configure a custom button for each direction."
@ -899,6 +926,8 @@ void ConfigBox::CreateGUIControls()
m_sSettings[i]->Add(m_gExtrasettings[i], 0, wxEXPAND | wxALL, 0);
m_sSettings[i]->Add(m_gGenSettings[i], 0, wxEXPAND | wxLEFT, 5);
m_sSettings[i]->Add(m_gGenSettingsID[i], 0, wxEXPAND | wxLEFT, 5);
m_sSettings[i]->Add(m_gRumble[i], 0, wxEXPAND | wxLEFT, 5);
// -------------------------
//////////////////////////// General settings

View File

@ -112,16 +112,21 @@ class ConfigBox : public wxDialog
wxStaticBoxSizer *m_gGenSettings[4];
wxStaticBoxSizer *m_gGenSettingsID[4];
wxGridBagSizer * m_gGBGenSettings[4];
wxGridBagSizer *m_gGBGenSettings[4];
wxCheckBox *m_CBSaveByID[4], *m_CBShowAdvanced[4];
wxStaticText *m_TSControltype[4], *m_TSTriggerType[4];
wxStaticBoxSizer *m_gStatusIn[4], *m_gStatusInSettings[4], *m_gStatusAdvancedSettings[4]; // Advanced settings
wxBoxSizer *m_gStatusInSettingsH[4];
wxGridBagSizer * m_GBAdvancedMainStick[4];
wxGridBagSizer *m_GBAdvancedMainStick[4];
wxStaticText *m_TStatusIn[4], *m_TStatusOut[4], *m_STDiagonal[4];
wxComboBox *m_CoBDiagonal[4]; wxCheckBox *m_CBS_to_C[4];
wxCheckBox *m_CBCheckFocus[4], *m_AdvancedMapFilter[4];
wxCheckBox *m_Rumble[4]; // Rumble settings
wxComboBox *m_RStrength[4];
wxStaticBoxSizer *m_gRumble[4];
wxGridBagSizer *m_gGBRumble[4];
wxStaticBoxSizer *m_gStatusTriggers[4]; // Triggers
wxStaticText *m_TStatusTriggers[4];
@ -218,6 +223,8 @@ class ConfigBox : public wxDialog
IDG_CONTROLLERTYPE, IDC_CONTROLTYPE, IDC_TRIGGERTYPE, // Controller type
IDC_SAVEBYID, IDC_SHOWADVANCED, // Settings
IDC_ENABLERUMBLE, IDC_RUMBLESTRENGTH, IDT_RUMBLESTRENGTH, // Rumble
ID_INSTATUS1, ID_INSTATUS2, ID_INSTATUS3, ID_INSTATUS4, // Advanced status
ID_STATUSBMP1, ID_STATUSBMP2, ID_STATUSBMP3, ID_STATUSBMP4,

View File

@ -80,11 +80,12 @@ void ConfigBox::UpdateGUIButtonMapping(int controller)
m_CoBDiagonal[controller]->SetValue(wxString::FromAscii(PadMapping[controller].SDiagonal.c_str()));
m_CBS_to_C[controller]->SetValue(PadMapping[controller].bSquareToCircle);
m_AdvancedMapFilter[controller]->SetValue(g_Config.bNoTriggerFilter);
// Update Rumble checkbox
m_Rumble[controller]->SetValue(PadMapping[controller].rumble);
#ifdef RERECORDING
m_CheckRecording[controller]->SetValue(g_Config.bRecording);
m_CheckPlayback[controller]->SetValue(g_Config.bPlayback);
#endif
//LogMsg("m_TriggerType[%i] = %i\n", controller, PadMapping[controller].triggertype);
// Update D-Pad
if(PadMapping[controller].controllertype == InputCommon::CTL_DPAD_HAT)
@ -99,8 +100,6 @@ void ConfigBox::UpdateGUIButtonMapping(int controller)
tmp << PadMapping[controller].dpad2[InputCommon::CTL_D_PAD_RIGHT]; m_JoyDpadRight[controller]->SetValue(tmp); tmp.clear();
}
// Replace "-1" with "" in the GUI controls
//if(ControlsCreated) ToBlank();
}
/* Populate the PadMapping array with the dialog items settings (for example

View File

@ -37,32 +37,29 @@
//////////////////////////////////////////////////////////////////////////////////////////
// Enable or disable rumble. Set USE_RUMBLE_DINPUT_HACK in nJoy.h
// Enable or disable rumble.
// ¯¯¯¯¯¯¯¯¯
#ifdef USE_RUMBLE_DINPUT_HACK
bool g_rumbleEnable = FALSE;
#endif
// Rumble in windows
#ifdef _WIN32
#ifdef USE_RUMBLE_DINPUT_HACK
LPDIRECTINPUT8 g_pDI = NULL;
LPDIRECTINPUTDEVICE8 g_pDevice = NULL;
LPDIRECTINPUTEFFECT g_pEffect = NULL;
DWORD g_dwNumForceFeedbackAxis = 0;
INT g_nXForce = 0;
INT g_nYForce = 0;
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; } }
HRESULT InitDirectInput(HWND hDlg);
//VOID FreeDirectInput();
BOOL CALLBACK EnumFFDevicesCallback(const DIDEVICEINSTANCE* pInst, VOID* pContext);
BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext);
HRESULT SetDeviceForcesXY();
#endif
void SetDeviceForcesXY(int pad, int nXYForce);
LPDIRECTINPUT8 g_Rumble; // DInput Rumble object
RUMBLE pRumble[4]; // 4 GC Rumble Pads
extern InputCommon::CONTROLLER_MAPPING PadMapping[4];
#elif defined(__linux__)
#include <sys/types.h>
@ -74,101 +71,36 @@ bool g_rumbleEnable = FALSE;
struct ff_effect effect;
bool CanRumble = false;
#endif
//////////////////////
// Set PAD rumble. Explanation: Stop = 0, Rumble = 1
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
void PAD_Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength)
{
//if (_numPAD > 0)
// return;
// SDL can't rumble the gamepad so we need to use platform specific code
#ifdef _WIN32
#ifdef USE_RUMBLE_DINPUT_HACK
static int a = 0;
if ((_uType == 0) || (_uType == 2))
{
a = 0;
}
else if (_uType == 1)
{
a = _uStrength > 2 ? 8000 : 0;
}
a = int ((float)a * 0.96f);
if (!g_rumbleEnable)
{
a = 0;
}
else
{
g_nYForce = a;
SetDeviceForcesXY();
}
#endif
#elif defined(__linux__)
struct input_event event;
if (CanRumble)
{
if (_uType == 1)
{
event.type = EV_FF;
event.code = effect.id;
event.value = 1;
if (write(fd, (const void*) &event, sizeof(event)) == -1) {
perror("Play effect");
exit(1);
}
}
if ((_uType == 0) || (_uType == 2))
{
event.type = EV_FF;
event.code = effect.id;
event.value = 0;
if (write(fd, (const void*) &event, sizeof(event)) == -1) {
perror("Stop effect");
exit(1);
}
}
}
#endif
}
// Use PAD rumble
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
void Pad_Use_Rumble(u8 _numPAD, SPADStatus* _pPADStatus)
void Pad_Use_Rumble(u8 _numPAD)//, SPADStatus* _pPADStatus)
{
#ifdef _WIN32
#ifdef USE_RUMBLE_DINPUT_HACK
// Enable or disable rumble
if (PadState[_numPAD].halfpress)
if (!g_pDI)
if (FAILED(InitDirectInput(m_hWnd)))
{
MessageBox(NULL, SDL_GetError(), "Could not initialize DirectInput!", MB_ICONERROR);
g_rumbleEnable = FALSE;
//return;
}
else
{
g_rumbleEnable = TRUE;
if (PadMapping[_numPAD].rumble) {
if (!g_Rumble) {
HWND rumble_hWnd = GetParent(m_hWnd);
HWND TopLevel = GetParent(rumble_hWnd);
// Support both rendering to main window and not.
if (GetForegroundWindow() == TopLevel)
rumble_hWnd = TopLevel;
if (GetForegroundWindow() == m_hWnd)
rumble_hWnd = m_hWnd;
if (FAILED(InitRumble(rumble_hWnd)))
PanicAlert("Could not initialize Rumble!");
} else {
// Acquire gamepad
if (pRumble[_numPAD].g_pDevice != NULL)
pRumble[_numPAD].g_pDevice->Acquire();
}
}
if (g_rumbleEnable)
{
g_pDevice->Acquire();
if (g_pEffect) g_pEffect->Start(1, 0);
}
#endif
#elif defined(__linux__)
if (!fd)
{
@ -204,191 +136,257 @@ void Pad_Use_Rumble(u8 _numPAD, SPADStatus* _pPADStatus)
#endif
}
////////////////////////////////////////////////////
// Set PAD rumble. Explanation: Stop = 0, Rumble = 1
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
void PAD_Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength)
{
Pad_Use_Rumble(_numPAD);
// SDL can't rumble the gamepad so we need to use platform specific code
#ifdef _WIN32
int a = 0;
if (_uType == 1)
{
// it looks like _uStrength is equal to 3 everytime anyway...
a = _uStrength > 2 ? (1000*(g_Config.RumbleStrength + 1)) : 0;
a = a > 10000 ? 10000 : a;
}
// a = int ((float)a * 0.96f);
// What is this for ?
// else if ((_uType == 0) || (_uType == 2))
if (PadMapping[_numPAD].rumble) // rumble activated
{
// Start Effect
SetDeviceForcesXY(_numPAD, a);
}
#elif defined(__linux__)
struct input_event event;
if (CanRumble)
{
if (_uType == 1)
{
event.type = EV_FF;
event.code = effect.id;
event.value = 1;
if (write(fd, (const void*) &event, sizeof(event)) == -1) {
perror("Play effect");
exit(1);
}
}
if ((_uType == 0) || (_uType == 2))
{
event.type = EV_FF;
event.code = effect.id;
event.value = 0;
if (write(fd, (const void*) &event, sizeof(event)) == -1) {
perror("Stop effect");
exit(1);
}
}
}
#endif
}
#ifdef _WIN32
//////////////////////////////////////////////////////////////////////////////////////////
// Rumble stuff :D!
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//
#ifdef USE_RUMBLE_DINPUT_HACK
HRESULT InitDirectInput( HWND hDlg )
HRESULT InitRumble(HWND hWnd)
{
DIPROPDWORD dipdw;
HRESULT hr;
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_pDI, NULL)))
{
return 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 force feedback device we can use
if (FAILED(hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumFFDevicesCallback, NULL, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK)))
{
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;
if (NULL == g_pDevice)
{
MessageBox(NULL, "Force feedback device not found. nJoy will now disable rumble." ,"FFConst" , MB_ICONERROR | MB_OK);
g_rumbleEnable = FALSE;
return S_OK;
}
for (int i=0; i<4; i++)
{
if (NULL == pRumble[i].g_pDevice)
PadMapping[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.
// Set the data format to "simple joystick" - a predefined data format. A
// data format specifies which controls on a device we are interested in,
// and how they should be reported.
//
// This tells DirectInput that we will be passing a DIJOYSTATE structure to
// IDirectInputDevice8::GetDeviceState(). Even though we won't actually do
// it in this sample. But setting the data format is important so that the
// DIJOFS_* values work properly.
if (FAILED(hr = g_pDevice->SetDataFormat(&c_dfDIJoystick)))
return hr;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = FALSE;
// Set the cooperative level to let DInput know how this device should
// interact with the system and with other DInput applications.
// Exclusive access is required in order to perform force feedback.
//if (FAILED(hr = g_pDevice->SetCooperativeLevel(hDlg, DISCL_EXCLUSIVE | DISCL_FOREGROUND)))
// 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);
if (FAILED(hr = g_pDevice->SetCooperativeLevel(hDlg, DISCL_EXCLUSIVE | DISCL_FOREGROUND)))
{
return hr;
}
PadMapping[i].rumble = false; // Disable Rumble for this pad
// Since we will be playing force feedback effects, we should disable the
// auto-centering spring.
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = FALSE;
continue; // Next pad
}
if (FAILED(hr = g_pDevice->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph)))
return hr;
if (pRumble[i].g_dwNumForceFeedbackAxis > 2)
pRumble[i].g_dwNumForceFeedbackAxis = 2;
// Enumerate and count the axes of the joystick
if (FAILED(hr = g_pDevice->EnumObjects(EnumAxesCallback, (VOID*)&g_dwNumForceFeedbackAxis, DIDFT_AXIS)))
return hr;
DWORD _rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y};
long rglDirection[2] = {0, 0};
DICONSTANTFORCE cf = {0};
// This simple sample only supports one or two axis joysticks
if (g_dwNumForceFeedbackAxis > 2)
g_dwNumForceFeedbackAxis = 2;
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;
// This application needs only one effect: Applying raw forces.
DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y};
LONG rglDirection[2] = {0, 0};
DICONSTANTFORCE cf = {0};
// Create the prepared effect
if (FAILED(hr = pRumble[i].g_pDevice->CreateEffect(GUID_ConstantForce, &pRumble[i].eff, &pRumble[i].g_pEffect, NULL)))
return hr;
if (pRumble[i].g_pEffect == NULL)
return E_FAIL;
}
}
DIEFFECT eff;
ZeroMemory(&eff, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE;
eff.dwSamplePeriod = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0;
eff.cAxes = g_dwNumForceFeedbackAxis;
eff.rgdwAxes = rgdwAxes;
eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof( DICONSTANTFORCE );
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0;
return S_OK;
}
// Create the prepared effect
if (FAILED(hr = g_pDevice->CreateEffect(GUID_ConstantForce, &eff, &g_pEffect, NULL)))
{
return hr;
}
void SetDeviceForcesXY(int npad, int nXYForce)
{
// Security check
if (pRumble[npad].g_pDevice == NULL)
return;
if (NULL == g_pEffect)
return E_FAIL;
// 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;
return S_OK;
// 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 = 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, PadMapping[0].ID, PadMapping[1].ID, PadMapping[2].ID, PadMapping[3].ID);
for (int i=0; i<4; i++)
{
if (PadMapping[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
if (PadMapping[i].enabled) // && PadMapping[i].rumble
{
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 FreeDirectInput()
{
// Unacquire the device one last time just in case
// the app tried to exit while the device is still acquired.
if (g_pDevice)
g_pDevice->Unacquire();
// Release any DirectInput objects.
SAFE_RELEASE(g_pEffect);
SAFE_RELEASE(g_pDevice);
SAFE_RELEASE(g_pDI);
for (int i=0; i<4; i++) // Free all pads
{
if (pRumble[i].g_pDevice) {
pRumble[i].g_pEffect->Stop();
pRumble[i].g_pDevice->Unacquire();
}
SAFE_RELEASE(pRumble[i].g_pEffect);
SAFE_RELEASE(pRumble[i].g_pDevice);
}
SAFE_RELEASE(g_Rumble); // Rumble object
}
BOOL CALLBACK EnumFFDevicesCallback( const DIDEVICEINSTANCE* pInst, VOID* pContext )
{
LPDIRECTINPUTDEVICE8 pDevice;
HRESULT hr;
// Obtain an interface to the enumerated force feedback device.
hr = g_pDI->CreateDevice(pInst->guidInstance, &pDevice, NULL);
// If it failed, then we can't use this device for some bizarre reason.
// (Maybe the user unplugged it while we were in the middle of enumerating it.) So continue enumerating
if (FAILED(hr))
return DIENUM_CONTINUE;
// We successfully created an IDirectInputDevice8. So stop looking for another one.
g_pDevice = pDevice;
return DIENUM_STOP;
}
BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext)
{
DWORD* pdwNumForceFeedbackAxis = (DWORD*)pContext;
if ((pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0)
(*pdwNumForceFeedbackAxis)++;
return DIENUM_CONTINUE;
}
HRESULT SetDeviceForcesXY()
{
// Modifying an effect is basically the same as creating a new one, except you need only specify the parameters you are modifying
LONG rglDirection[2] = { 0, 0 };
DICONSTANTFORCE cf;
if (g_dwNumForceFeedbackAxis == 1)
{
// If only one force feedback axis, then apply only one direction and keep the direction at zero
cf.lMagnitude = g_nXForce;
rglDirection[0] = 0;
}
else
{
// If two force feedback axis, then apply magnitude from both directions
rglDirection[0] = g_nXForce;
rglDirection[1] = g_nYForce;
cf.lMagnitude = (DWORD)sqrt((double)g_nXForce * (double)g_nXForce + (double)g_nYForce * (double)g_nYForce );
}
DIEFFECT eff;
ZeroMemory(&eff, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.cAxes = g_dwNumForceFeedbackAxis;
eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0;
// Now set the new parameters and start the effect immediately.
return g_pEffect->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START);
}
#endif
#endif

View File

@ -6,7 +6,7 @@
//
// Author: Falcon4ever (nJoy@falcon4ever.com)
// Site: www.multigesture.net
// Copyright (C) 2003-2008 Dolphin Project.
// Copyright (C) 2003-2009 Dolphin Project.
//
//////////////////////////////////////////////////////////////////////////////////////////
//
@ -38,6 +38,7 @@
The StrangeHack in ConfigAdvanced.cpp doesn't work in Linux, it still wont resize the
window correctly. So currently in Linux you have to have advanced controls enabled when
you open the window to see them.
// TODO : we should not need a Hack in the first place :/
////////////////////////*/
@ -80,7 +81,6 @@
// Variables
// ¯¯¯¯¯¯¯¯¯
// Rumble in windows
#define _EXCLUDE_MAIN_ // Avoid certain declarations in nJoy.h
FILE *pFile;
HINSTANCE nJoy_hInst = NULL;
@ -95,14 +95,11 @@ int NumPads = 0, NumGoodPads = 0, LastPad = 0;
SPADInitialize *g_PADInitialize = NULL;
PLUGIN_GLOBALS* globals = NULL;
// Rumble
#ifdef _WIN32
#elif defined(__linux__)
// Rumble
#if defined(__linux__)
extern int fd;
#endif
//////////////////////////////////////////////////////////////////////////////////////////
// wxWidgets
// ¯¯¯¯¯¯¯¯¯
@ -249,15 +246,15 @@ void Initialize(void *init)
INFO_LOG(CONSOLE, "Initialize: %i\n", SDL_WasInit(0));
g_PADInitialize = (SPADInitialize*)init;
g_EmulatorRunning = true;
#ifdef _WIN32
m_hWnd = (HWND)g_PADInitialize->hWnd;
#endif
#ifdef _DEBUG
DEBUG_INIT();
#endif
#ifdef _WIN32
m_hWnd = (HWND)g_PADInitialize->hWnd;
#endif
// Populate joyinfo for all attached devices if the configuration window is not already open
#if defined(HAVE_WX) && HAVE_WX
if(!m_frame)
@ -327,9 +324,7 @@ void Shutdown()
g_PADInitialize = NULL;
#ifdef _WIN32
#ifdef USE_RUMBLE_DINPUT_HACK
FreeDirectInput();
#endif
FreeDirectInput();
#elif defined(__linux__)
close(fd);
#endif
@ -415,6 +410,7 @@ void PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus)
return;
}
#endif
// ----------------------
// Clear pad status
@ -426,6 +422,7 @@ void PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus)
// Get type
int TriggerType = PadMapping[_numPAD].triggertype;
int TriggerValue = PadState[_numPAD].halfpress ? 100 : 255;
///////////////////////////////////////////////////
// The analog controls
@ -440,7 +437,7 @@ void PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus)
int TriggerRight = PadState[_numPAD].axis[InputCommon::CTL_R_SHOULDER];
// Check if we should make adjustments
if(PadMapping[_numPAD].bSquareToCircle)
if (PadMapping[_numPAD].bSquareToCircle)
{
std::vector<int> main_xy = InputCommon::Square2Circle(i_main_stick_x, i_main_stick_y, _numPAD, PadMapping[_numPAD].SDiagonal);
i_main_stick_x = main_xy.at(0);
@ -454,54 +451,61 @@ void PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus)
u8 sub_stick_y = InputCommon::Pad_Convert(i_sub_stick_y);
// Convert the triggers values, if we are using analog triggers at all
if(PadMapping[_numPAD].triggertype == InputCommon::CTL_TRIGGER_SDL)
if (PadMapping[_numPAD].triggertype == InputCommon::CTL_TRIGGER_SDL)
{
if(PadMapping[_numPAD].buttons[InputCommon::CTL_L_SHOULDER] >= 1000) TriggerLeft = InputCommon::Pad_Convert(TriggerLeft);
if(PadMapping[_numPAD].buttons[InputCommon::CTL_R_SHOULDER] >= 1000) TriggerRight = InputCommon::Pad_Convert(TriggerRight);
if (PadMapping[_numPAD].buttons[InputCommon::CTL_L_SHOULDER] >= 1000) TriggerLeft = InputCommon::Pad_Convert(TriggerLeft);
if (PadMapping[_numPAD].buttons[InputCommon::CTL_R_SHOULDER] >= 1000) TriggerRight = InputCommon::Pad_Convert(TriggerRight);
}
// Set Deadzones (perhaps out of function?)
int deadzone = (int)(((float)(128.00/100.00)) * (float)(PadMapping[_numPAD].deadzone + 1));
int deadzone2 = (int)(((float)(-128.00/100.00)) * (float)(PadMapping[_numPAD].deadzone + 1));
// Set Deadzone
float deadzone = (128.00 * (float)(PadMapping[_numPAD].deadzone + 1.00)) / 100.00;
float distance_main = (float)sqrt((float)(main_stick_x * main_stick_x) + (float)(main_stick_y * main_stick_y));
float distance_sub = (float)sqrt((float)(sub_stick_x * sub_stick_x) + (float)(sub_stick_y * sub_stick_y));
// Send values to Dolpin if they are outside the deadzone
if ((main_stick_x < deadzone2) || (main_stick_x > deadzone)) _pPADStatus->stickX = main_stick_x;
if ((main_stick_y < deadzone2) || (main_stick_y > deadzone)) _pPADStatus->stickY = main_stick_y;
if ((sub_stick_x < deadzone2) || (sub_stick_x > deadzone)) _pPADStatus->substickX = sub_stick_x;
if ((sub_stick_y < deadzone2) || (sub_stick_y > deadzone)) _pPADStatus->substickY = sub_stick_y;
if (distance_main > deadzone)
{
_pPADStatus->stickX = main_stick_x;
_pPADStatus->stickY = main_stick_y;
}
if (distance_sub > deadzone)
{
_pPADStatus->substickX = sub_stick_x;
_pPADStatus->substickY = sub_stick_y;
}
///////////////////////////////////////////////////
// The L and R triggers
// -----------
int TriggerValue = 255;
if (PadState[_numPAD].halfpress) TriggerValue = 100;
// -----------
_pPADStatus->button |= PAD_USE_ORIGIN; // Neutral value, no button pressed
// Neutral value, no button pressed
_pPADStatus->button |= PAD_USE_ORIGIN;
// Check if the digital L button is pressed
if (PadState[_numPAD].buttons[InputCommon::CTL_L_SHOULDER])
{
_pPADStatus->button |= PAD_TRIGGER_L;
if (!PadState[_numPAD].halfpress)
_pPADStatus->button |= PAD_TRIGGER_L;
_pPADStatus->triggerLeft = TriggerValue;
}
// no the digital L button is not pressed, but the analog left trigger is
else if(TriggerLeft > 0)
else if (TriggerLeft > 0)
_pPADStatus->triggerLeft = TriggerLeft;
// Check if the digital R button is pressed
if (PadState[_numPAD].buttons[InputCommon::CTL_R_SHOULDER])
{
_pPADStatus->button |= PAD_TRIGGER_R;
if (!PadState[_numPAD].halfpress)
_pPADStatus->button |= PAD_TRIGGER_R;
_pPADStatus->triggerRight = TriggerValue;
}
// no the digital R button is not pressed, but the analog right trigger is
else if(TriggerRight > 0)
else if (TriggerRight > 0)
_pPADStatus->triggerRight = TriggerRight;
// Update the buttons in analog mode to
if(TriggerLeft == 0xff) _pPADStatus->button |= PAD_TRIGGER_L;
if(TriggerRight == 0xff) _pPADStatus->button |= PAD_TRIGGER_R;
// Update the buttons in analog mode too
if (TriggerLeft > 0xf0) _pPADStatus->button |= PAD_TRIGGER_L;
if (TriggerRight > 0xf0) _pPADStatus->button |= PAD_TRIGGER_R;
///////////////////////////////////////////////////
@ -548,9 +552,6 @@ void PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus)
// Update error code
_pPADStatus->err = PAD_ERR_NONE;
// Use rumble
Pad_Use_Rumble(_numPAD, _pPADStatus);
// -------------------------------------------
// Rerecording
// ----------------------

View File

@ -33,15 +33,6 @@
#ifndef __NJOY_h__
#define __NJOY_h__
//////////////////////////////////////////////////////////////////////////////////////////
// Settings
// ¯¯¯¯¯¯¯¯¯¯
// Set this if you want to use the rumble 'hack' for controller one
//#define USE_RUMBLE_DINPUT_HACK
//////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// Includes
// ¯¯¯¯¯¯¯¯¯¯
@ -71,13 +62,12 @@
#define DIRECTINPUT_VERSION 0x0800
#define WIN32_LEAN_AND_MEAN
#ifdef USE_RUMBLE_DINPUT_HACK
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "winmm.lib")
#include <dinput.h>
VOID FreeDirectInput(); // Needed in both nJoy.cpp and Rumble.cpp
#endif
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "winmm.lib")
#include <dinput.h>
void FreeDirectInput(); // Needed in both nJoy.cpp and Rumble.cpp
#endif // _WIN32
#ifdef _WIN32
@ -142,11 +132,10 @@ void DEBUG_INIT();
void DEBUG_QUIT();
bool IsFocus();
bool ReloadDLL();
#ifdef _WIN32
HRESULT InitRumble(HWND hWnd);
#endif
void Pad_Use_Rumble(u8 _numPAD, SPADStatus* _pPADStatus); // Rumble
//void SaveConfig();
//void LoadConfig();
////////////////////////////////