diff --git a/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.cpp b/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.cpp index b9706fac71..b056235e91 100644 --- a/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.cpp +++ b/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.cpp @@ -42,6 +42,29 @@ CONTROLLER_STATE joystate[4]; CONTROLLER_MAPPING joysticks[4]; bool emulator_running = FALSE; +// Handle to window +HWND m_hWnd; + +// Rumble in windows +#ifdef _WIN32 +LPDIRECTINPUT8 g_pDI = NULL; +LPDIRECTINPUTDEVICE8 g_pDevice = NULL; +LPDIRECTINPUTEFFECT g_pEffect = NULL; + +DWORD g_dwNumForceFeedbackAxis = 0; +INT g_nXForce = 0; +INT g_nYForce = 0; + +bool g_rumbleEnable = FALSE; +#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 + ////////////////////////////////////////////////////////////////////////////////////////// // wxWidgets // ŻŻŻŻŻŻŻŻŻ @@ -219,6 +242,10 @@ void PAD_Initialize(SPADInitialize _PADInitialize) return; } + #ifdef _WIN32 + m_hWnd = (HWND)_PADInitialize.hWnd; + #endif + LoadConfig(); // Load joystick mapping if(joysticks[0].enabled) @@ -253,6 +280,10 @@ void PAD_Shutdown() delete [] joyinfo; emulator_running = FALSE; + + #ifdef _WIN32 + FreeDirectInput(); + #endif } // Set PAD status @@ -261,7 +292,7 @@ void PAD_GetStatus(BYTE _numPAD, SPADStatus* _pPADStatus) { if(!joysticks[_numPAD].enabled) return; - + // clear pad status memset(_pPADStatus, 0, sizeof(SPADStatus)); @@ -366,13 +397,65 @@ void PAD_GetStatus(BYTE _numPAD, SPADStatus* _pPADStatus) } _pPADStatus->err = PAD_ERR_NONE; + + + #ifdef _WIN32 + if(joystate[_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; + #endif + + if (g_rumbleEnable) + { + g_pDevice->Acquire(); + + if(g_pEffect) + g_pEffect->Start(1, 0); + } } // Set PAD rumble // ŻŻŻŻŻŻŻŻŻŻŻŻŻŻ +// (Stop=0, Rumble=1) void PAD_Rumble(BYTE _numPAD, unsigned int _uType, unsigned int _uStrength) { + if(_numPAD > 0) + return; + // not supported by SDL + // So we need to use platform specific stuff + #ifdef _WIN32 + 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 } // Set PAD attached pads @@ -617,3 +700,188 @@ void LoadConfig() file.Get(SectionName, "controllertype", &joysticks[i].controllertype, 0); } } + + +#ifdef _WIN32 +////////////////////////////////////////////////////////////////////////////////////////// +// Rumble stuff :D! +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ +// + +HRESULT InitDirectInput( HWND hDlg ) +{ + 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; + } + + // 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; + } + + 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; + } + + // 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; + + // 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(FAILED(hr = g_pDevice->SetCooperativeLevel(hDlg, DISCL_EXCLUSIVE | DISCL_FOREGROUND))) + { + return hr; + } + + // 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; + + if(FAILED(hr = g_pDevice->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph))) + return hr; + + // Enumerate and count the axes of the joystick + if(FAILED(hr = g_pDevice->EnumObjects(EnumAxesCallback, (VOID*)&g_dwNumForceFeedbackAxis, DIDFT_AXIS))) + return hr; + + // This simple sample only supports one or two axis joysticks + if(g_dwNumForceFeedbackAxis > 2) + g_dwNumForceFeedbackAxis = 2; + + // This application needs only one effect: Applying raw forces. + DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y}; + LONG rglDirection[2] = {0, 0}; + DICONSTANTFORCE cf = {0}; + + 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; + + // Create the prepared effect + if(FAILED(hr = g_pDevice->CreateEffect(GUID_ConstantForce, &eff, &g_pEffect, NULL))) + { + return hr; + } + + if(NULL == g_pEffect) + return E_FAIL; + + return S_OK; +} + +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); +} + +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 \ No newline at end of file diff --git a/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.h b/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.h index 159ad1ea93..98ce57e394 100644 --- a/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.h +++ b/Source/Plugins/Plugin_nJoy_SDL/Src/nJoy.h @@ -34,9 +34,11 @@ #ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS - +#define DIRECTINPUT_VERSION 0x0800 #define WIN32_LEAN_AND_MEAN #include +#include +#include // used for rumble #endif #include @@ -65,6 +67,11 @@ #ifdef _WIN32 #pragma comment(lib, "SDL.lib") #pragma comment(lib, "comctl32.lib") + +// Required for the rumble part +#pragma comment(lib, "dxguid.lib") +#pragma comment(lib, "dinput8.lib") +#pragma comment(lib, "winmm.lib") #endif ////////////////////////////////////////////////////////////////////////////////////////// @@ -78,6 +85,14 @@ #define RELYEAR "2008" #define THANKYOU "`plot`, Absolute0, Aprentice, Bositman, Brice, ChaosCode, CKemu, CoDeX, Dave2001, dn, drk||Raziel, Florin, Gent, Gigaherz, Hacktarux, JegHegy, Linker, Linuzappz, Martin64, Muad, Knuckles, Raziel, Refraction, Rudy_x, Shadowprince, Snake785, Saqib, vEX, yaz0r, Zilmar, Zenogais and ZeZu." +#ifdef _WIN32 +// Rumble stuff :D! +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +// Structures +// ŻŻŻŻŻŻŻŻŻŻ + struct CONTROLLER_STATE{ // GC PAD INFO/STATE int buttons[8]; // Amount of buttons (A B X Y Z, L-Trigger R-Trigger Start) might need to change the triggers buttons int dpad; // 1 HAT (8 directions + neutral) @@ -143,6 +158,7 @@ enum CTL_D_PAD_LEFT, CTL_D_PAD_RIGHT }; + ////////////////////////////////////////////////////////////////////////////////////////// // Custom Functions // ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ