|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
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, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
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 for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
// plInputManager.cpp
|
|
|
|
#define DIRECTINPUT_VERSION 0x0800
|
|
|
|
#include <dinput.h>
|
|
|
|
|
|
|
|
#include "plInputManager.h"
|
|
|
|
#include "plPipeline.h"
|
|
|
|
#include "plInputDevice.h"
|
|
|
|
#include "plDInputDevice.h"
|
|
|
|
#include "plMessage/plInputEventMsg.h"
|
|
|
|
#include "plInputInterfaceMgr.h"
|
|
|
|
#include "hsStream.h"
|
|
|
|
#include "pnKeyedObject/plKey.h"
|
|
|
|
#include "pnKeyedObject/plFixedKey.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
#include "hsTimer.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "pnMessage/plTimeMsg.h"
|
|
|
|
#include "pnMessage/plCmdIfaceModMsg.h"
|
|
|
|
#include "pnMessage/plPlayerPageMsg.h"
|
|
|
|
|
|
|
|
hsBool plInputManager::fUseDInput = false;
|
|
|
|
uint8_t plInputManager::bRecenterMouse = 0;
|
|
|
|
HWND plInputManager::fhWnd = nil;
|
|
|
|
#define NUM_ACTIONS 17
|
|
|
|
|
|
|
|
struct plDIDevice
|
|
|
|
{
|
|
|
|
plDIDevice() : fDevice(nil), fCaps(nil) {;}
|
|
|
|
plDIDevice(IDirectInputDevice8* _device) : fCaps(nil) { fDevice = _device;}
|
|
|
|
IDirectInputDevice8* fDevice;
|
|
|
|
DIDEVCAPS* fCaps;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct plDInput
|
|
|
|
{
|
|
|
|
plDInput() :
|
|
|
|
fDInput(nil),
|
|
|
|
fActionFormat(nil)
|
|
|
|
{;}
|
|
|
|
IDirectInput8* fDInput;
|
|
|
|
hsTArray<plDIDevice*> fSticks;
|
|
|
|
DIACTIONFORMAT* fActionFormat;
|
|
|
|
};
|
|
|
|
|
|
|
|
class plDInputMgr
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
plDInputMgr();
|
|
|
|
~plDInputMgr();
|
|
|
|
|
|
|
|
void Init(HINSTANCE hInst, HWND hWnd);
|
|
|
|
void Update();
|
|
|
|
void AddDevice(IDirectInputDevice8* device);
|
|
|
|
void ConfigureDevice();
|
|
|
|
virtual hsBool MsgReceive(plMessage* msg);
|
|
|
|
|
|
|
|
// dinput callback functions
|
|
|
|
static int __stdcall EnumGamepadCallback(const DIDEVICEINSTANCE* device, void* pRef);
|
|
|
|
// I should be using these but they don't work...
|
|
|
|
// static int __stdcall SetAxisRange(const DIDEVICEOBJECTINSTANCE* obj, void* pRef);
|
|
|
|
// static int __stdcall EnumSuitableDevices(const struct DIDEVICEINSTANCEA* devInst, struct IDirectInputDevice8* dev, unsigned long why, unsigned long devRemaining, void* pRef);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
plDInput* fDI;
|
|
|
|
hsTArray<plDInputDevice*> fInputDevice;
|
|
|
|
static DIACTION fActionMap[];
|
|
|
|
HWND fhWnd;
|
|
|
|
};
|
|
|
|
// function pointers to dinput callbacks
|
|
|
|
typedef int (__stdcall * Pfunc1) (const DIDEVICEINSTANCE* device, void* pRef);
|
|
|
|
// I should need these...
|
|
|
|
//typedef int (__stdcall * Pfunc2) (const DIDEVICEOBJECTINSTANCE* device, void* pRef);
|
|
|
|
//typedef int (__stdcall * Pfunc3) (const struct DIDEVICEINSTANCEA* devInst, struct IDirectInputDevice8* dev, unsigned long why, unsigned long devRemaining, void* pRef);
|
|
|
|
|
|
|
|
|
|
|
|
plInputManager* plInputManager::fInstance = nil;
|
|
|
|
|
|
|
|
plInputManager::plInputManager( HWND hWnd ) :
|
|
|
|
fDInputMgr(nil),
|
|
|
|
fInterfaceMgr(nil)
|
|
|
|
{
|
|
|
|
fhWnd = hWnd;
|
|
|
|
fInstance = this;
|
|
|
|
fActive = false;
|
|
|
|
fFirstActivated = false;
|
|
|
|
fMouseScale = 1.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
plInputManager::plInputManager() :
|
|
|
|
fDInputMgr(nil),
|
|
|
|
fInterfaceMgr(nil)
|
|
|
|
{
|
|
|
|
fInstance = this;
|
|
|
|
fActive = false;
|
|
|
|
fFirstActivated = false;
|
|
|
|
fMouseScale = 1.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
plInputManager::~plInputManager()
|
|
|
|
{
|
|
|
|
fInterfaceMgr->Shutdown();
|
|
|
|
fInterfaceMgr = nil;
|
|
|
|
|
|
|
|
for (int i = 0; i < fInputDevices.Count(); i++)
|
|
|
|
{
|
|
|
|
fInputDevices[i]->Shutdown();
|
|
|
|
delete(fInputDevices[i]);
|
|
|
|
}
|
|
|
|
if (fDInputMgr)
|
|
|
|
delete fDInputMgr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
void plInputManager::SetRecenterMouse(hsBool b)
|
|
|
|
{
|
|
|
|
if (b)
|
|
|
|
bRecenterMouse++;
|
|
|
|
else if (bRecenterMouse > 0)
|
|
|
|
bRecenterMouse--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plInputManager::RecenterCursor()
|
|
|
|
{
|
|
|
|
RECT rect;
|
|
|
|
GetClientRect(fhWnd, &rect);
|
|
|
|
POINT pt;
|
|
|
|
// pt.y = ( (rect.bottom - rect.top) / 2 ) / fInstance->fMouseScale;
|
|
|
|
// pt.x = ( (rect.right - rect.left) / 2 ) / fInstance->fMouseScale;
|
|
|
|
ClientToScreen(fhWnd, &pt);
|
|
|
|
SetCursorPos( pt.x, pt.y );
|
|
|
|
}
|
|
|
|
void plInputManager::CreateInterfaceMod(plPipeline* p)
|
|
|
|
{
|
|
|
|
fInterfaceMgr = TRACKED_NEW plInputInterfaceMgr();
|
|
|
|
fInterfaceMgr->Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plInputManager::InitDInput(HINSTANCE hInst, HWND hWnd)
|
|
|
|
{
|
|
|
|
if (fUseDInput)
|
|
|
|
{
|
|
|
|
fDInputMgr = TRACKED_NEW plDInputMgr;
|
|
|
|
fDInputMgr->Init(hInst, hWnd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plInputManager::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
for (int i=0; i<fInputDevices.Count(); i++)
|
|
|
|
if (fInputDevices[i]->MsgReceive(msg))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (fDInputMgr)
|
|
|
|
return fDInputMgr->MsgReceive(msg);
|
|
|
|
|
|
|
|
return hsKeyedObject::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plInputManager::Update()
|
|
|
|
{
|
|
|
|
if (fDInputMgr)
|
|
|
|
fDInputMgr->Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plInputManager::SetMouseScale( float s )
|
|
|
|
{
|
|
|
|
/* RECT rect;
|
|
|
|
POINT currPos;
|
|
|
|
|
|
|
|
|
|
|
|
// Gotta make sure to move the mouse to the correct new position for the scale
|
|
|
|
GetClientRect( fhWnd, &rect );
|
|
|
|
GetCursorPos( &currPos );
|
|
|
|
ScreenToClient( fhWnd, &currPos );
|
|
|
|
|
|
|
|
float x = (float)currPos.x / rect.right;
|
|
|
|
float y = (float)currPos.y / rect.bottom;
|
|
|
|
|
|
|
|
x *= fMouseScale; y *= fMouseScale;
|
|
|
|
|
|
|
|
fMouseScale = s;
|
|
|
|
|
|
|
|
// Refreshes all of the input devices so that they can reset mouse limits, etc
|
|
|
|
RECT rect2 = rect;
|
|
|
|
rect2.right /= fMouseScale;
|
|
|
|
rect2.bottom /= fMouseScale;
|
|
|
|
::MapWindowPoints( fhWnd, NULL, (POINT *)&rect2, 2 );
|
|
|
|
BOOL ret = ::ClipCursor( &rect );
|
|
|
|
|
|
|
|
// Now move the cursor to the right spot
|
|
|
|
|
|
|
|
currPos.x = ( x / fMouseScale ) * rect.right;
|
|
|
|
currPos.y = ( y / fMouseScale ) * rect.bottom;
|
|
|
|
|
|
|
|
ClientToScreen( fhWnd, &currPos );
|
|
|
|
SetCursorPos( currPos.x, currPos.y );
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sometimes the keyboard driver "helps" us translating a key involved in a key
|
|
|
|
// combo. For example pressing shif-numpad8 will actually generate a KEY_UP event,
|
|
|
|
// the same as the up arrow. This function undoes that translation.
|
|
|
|
plKeyDef plInputManager::UntranslateKey(plKeyDef key, hsBool extended)
|
|
|
|
{
|
|
|
|
if (!extended)
|
|
|
|
{
|
|
|
|
if (key == KEY_UP)
|
|
|
|
return KEY_NUMPAD8;
|
|
|
|
if (key == KEY_DOWN)
|
|
|
|
return KEY_NUMPAD2;
|
|
|
|
if (key == KEY_LEFT)
|
|
|
|
return KEY_NUMPAD4;
|
|
|
|
if (key == KEY_RIGHT)
|
|
|
|
return KEY_NUMPAD6;
|
|
|
|
}
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plInputManager::HandleWin32ControlEvent(UINT message, WPARAM Wparam, LPARAM Lparam, HWND hWnd)
|
|
|
|
{
|
|
|
|
if( !fhWnd )
|
|
|
|
fhWnd = hWnd;
|
|
|
|
|
|
|
|
hsBool bExtended;
|
|
|
|
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case SYSKEYDOWN:
|
|
|
|
case KEYDOWN:
|
|
|
|
{
|
|
|
|
bExtended = Lparam >> 24 & 1;
|
|
|
|
hsBool bRepeat = ((Lparam >> 29) & 0xf) != 0;
|
|
|
|
for (int i=0; i<fInputDevices.Count(); i++)
|
|
|
|
fInputDevices[i]->HandleKeyEvent( KEYDOWN, UntranslateKey((plKeyDef)Wparam, bExtended), true, bRepeat );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SYSKEYUP:
|
|
|
|
case KEYUP:
|
|
|
|
{
|
|
|
|
bExtended = Lparam >> 24 & 1;
|
|
|
|
for (int i=0; i<fInputDevices.Count(); i++)
|
|
|
|
fInputDevices[i]->HandleKeyEvent( KEYUP, UntranslateKey((plKeyDef)Wparam, bExtended), false, false );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CHAR_MSG:
|
|
|
|
{
|
|
|
|
// These are handled by KEYUP/KEYDOWN and should not be sent
|
|
|
|
// We don't like garbage getting in string fields
|
|
|
|
if (Wparam == KEY_BACKSPACE || Wparam == KEY_ESCAPE)
|
|
|
|
break;
|
|
|
|
|
|
|
|
BYTE scan = (BYTE)(Lparam >> 16);
|
|
|
|
UINT vkey = MapVirtualKey(scan, MAPVK_VSC_TO_VK);
|
|
|
|
|
|
|
|
bExtended = Lparam >> 24 & 1;
|
|
|
|
hsBool bRepeat = ((Lparam >> 29) & 0xf) != 0;
|
|
|
|
bool down = !(Lparam >> 31);
|
|
|
|
wchar_t ch = (wchar_t)Wparam;
|
|
|
|
// for the return key, Windows sends CR, but multiline text input fields want LF (CR is rendered as a wide horizontal space)
|
|
|
|
if (ch == 0x0D)
|
|
|
|
ch = 0x0A;
|
|
|
|
for (int i=0; i<fInputDevices.Count(); i++)
|
|
|
|
fInputDevices[i]->HandleKeyEvent( CHAR_MSG, (plKeyDef)vkey, down, bRepeat, ch );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MOUSEWHEEL:
|
|
|
|
{
|
|
|
|
plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg;
|
|
|
|
int zDelta = GET_WHEEL_DELTA_WPARAM(Wparam);
|
|
|
|
pMsg->SetWheelDelta((float)zDelta);
|
|
|
|
if (zDelta < 0)
|
|
|
|
pMsg->SetButton(kWheelNeg);
|
|
|
|
else
|
|
|
|
pMsg->SetButton(kWheelPos);
|
|
|
|
|
|
|
|
RECT rect;
|
|
|
|
GetClientRect(hWnd, &rect);
|
|
|
|
pMsg->SetXPos(LOWORD(Lparam) / (float)rect.right);
|
|
|
|
pMsg->SetYPos(HIWORD(Lparam) / (float)rect.bottom);
|
|
|
|
|
|
|
|
pMsg->Send();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MOUSEMOVE:
|
|
|
|
case L_BUTTONDN:
|
|
|
|
case L_BUTTONUP:
|
|
|
|
case R_BUTTONDN:
|
|
|
|
case R_BUTTONUP:
|
|
|
|
case L_BUTTONDBLCLK:
|
|
|
|
case R_BUTTONDBLCLK:
|
|
|
|
case M_BUTTONDN:
|
|
|
|
case M_BUTTONUP:
|
|
|
|
{
|
|
|
|
|
|
|
|
RECT rect;
|
|
|
|
GetClientRect(hWnd, &rect);
|
|
|
|
|
|
|
|
plIMouseXEventMsg* pXMsg = TRACKED_NEW plIMouseXEventMsg;
|
|
|
|
plIMouseYEventMsg* pYMsg = TRACKED_NEW plIMouseYEventMsg;
|
|
|
|
plIMouseBEventMsg* pBMsg = TRACKED_NEW plIMouseBEventMsg;
|
|
|
|
|
|
|
|
pXMsg->fWx = LOWORD(Lparam);
|
|
|
|
pXMsg->fX = (float)LOWORD(Lparam) / (float)rect.right;
|
|
|
|
pYMsg->fWy = HIWORD(Lparam);
|
|
|
|
pYMsg->fY = (float)HIWORD(Lparam) / (float)rect.bottom;
|
|
|
|
|
|
|
|
// Apply mouse scale
|
|
|
|
// pXMsg->fX *= fMouseScale;
|
|
|
|
// pYMsg->fY *= fMouseScale;
|
|
|
|
|
|
|
|
if (Wparam & MK_LBUTTON && message != L_BUTTONUP)
|
|
|
|
{
|
|
|
|
pBMsg->fButton |= kLeftButtonDown;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pBMsg->fButton |= kLeftButtonUp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Wparam & MK_RBUTTON && message != R_BUTTONUP)
|
|
|
|
{
|
|
|
|
pBMsg->fButton |= kRightButtonDown;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pBMsg->fButton |= kRightButtonUp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Wparam & MK_MBUTTON && message != M_BUTTONUP)
|
|
|
|
{
|
|
|
|
pBMsg->fButton |= kMiddleButtonDown;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pBMsg->fButton |= kMiddleButtonUp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( message == L_BUTTONDBLCLK )
|
|
|
|
pBMsg->fButton |= kLeftButtonDblClk; // We send the double clicks separately
|
|
|
|
if( message == R_BUTTONDBLCLK )
|
|
|
|
pBMsg->fButton |= kRightButtonDblClk;
|
|
|
|
|
|
|
|
for (int i=0; i<fInputDevices.Count(); i++)
|
|
|
|
{
|
|
|
|
fInputDevices[i]->MsgReceive(pXMsg);
|
|
|
|
fInputDevices[i]->MsgReceive(pYMsg);
|
|
|
|
fInputDevices[i]->MsgReceive(pBMsg);
|
|
|
|
}
|
|
|
|
POINT pt;
|
|
|
|
|
|
|
|
if (RecenterMouse() && (pXMsg->fX <= 0.1 || pXMsg->fX >= 0.9) )
|
|
|
|
{
|
|
|
|
pt.x = (rect.right - rect.left) / 2;
|
|
|
|
pt.y = HIWORD(Lparam);
|
|
|
|
ClientToScreen(hWnd, &pt);
|
|
|
|
SetCursorPos( pt.x, pt.y );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (RecenterMouse() && (pYMsg->fY <= 0.1 || pYMsg->fY >= 0.9) )
|
|
|
|
{
|
|
|
|
pt.y = (rect.bottom - rect.top) / 2;
|
|
|
|
pt.x = LOWORD(Lparam);
|
|
|
|
ClientToScreen(hWnd, &pt);
|
|
|
|
SetCursorPos( pt.x, pt.y );
|
|
|
|
}
|
|
|
|
if (RecenterMouse() && Lparam == 0)
|
|
|
|
{
|
|
|
|
pt.y = (rect.bottom - rect.top) / 2;
|
|
|
|
pt.x = (rect.right - rect.left) / 2;
|
|
|
|
ClientToScreen(hWnd, &pt);
|
|
|
|
SetCursorPos( pt.x, pt.y );
|
|
|
|
}
|
|
|
|
delete(pXMsg);
|
|
|
|
delete(pYMsg);
|
|
|
|
delete(pBMsg);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
|
|
{
|
|
|
|
bool activated = ( LOWORD( Wparam ) == WA_INACTIVE ) ? false : true;
|
|
|
|
Activate( activated );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Activate ////////////////////////////////////////////////////////////////
|
|
|
|
// Handles what happens when the app (window) activates/deactivates
|
|
|
|
|
|
|
|
void plInputManager::Activate( bool activating )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < fInputDevices.GetCount(); i++ )
|
|
|
|
fInputDevices[ i ]->HandleWindowActivate( activating, fhWnd );
|
|
|
|
|
|
|
|
fActive = activating;
|
|
|
|
fFirstActivated = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// AddInputDevice //////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plInputManager::AddInputDevice( plInputDevice *pDev )
|
|
|
|
{
|
|
|
|
fInputDevices.Append( pDev );
|
|
|
|
if( fFirstActivated )
|
|
|
|
pDev->HandleWindowActivate( fActive, fhWnd );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// dinput manager
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
plDInputMgr::plDInputMgr() :
|
|
|
|
fDI(nil)
|
|
|
|
{
|
|
|
|
fDI = TRACKED_NEW plDInput;
|
|
|
|
}
|
|
|
|
|
|
|
|
plDInputMgr::~plDInputMgr()
|
|
|
|
{
|
|
|
|
if (fDI)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < fDI->fSticks.Count(); i++)
|
|
|
|
{
|
|
|
|
plDIDevice* pD = fDI->fSticks[i];
|
|
|
|
pD->fDevice->Release();
|
|
|
|
delete(pD->fCaps);
|
|
|
|
delete(pD);
|
|
|
|
}
|
|
|
|
fDI->fSticks.SetCountAndZero(0);
|
|
|
|
delete(fDI->fActionFormat);
|
|
|
|
fDI->fDInput->Release();
|
|
|
|
for(int j = 0; j < fInputDevice.Count(); j++)
|
|
|
|
delete(fInputDevice[j]);
|
|
|
|
fInputDevice.SetCountAndZero(0);
|
|
|
|
delete fDI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plDInputMgr::Init(HINSTANCE hInst, HWND hWnd)
|
|
|
|
{
|
|
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&fDI->fDInput, NULL);
|
|
|
|
hsAssert(!hr, "failed to initialize directInput!");
|
|
|
|
// enumerate game controllers
|
|
|
|
Pfunc1 fPtr = &plDInputMgr::EnumGamepadCallback;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// set up the action mapping
|
|
|
|
fDI->fActionFormat = TRACKED_NEW DIACTIONFORMAT;
|
|
|
|
fDI->fActionFormat->dwSize = sizeof(DIACTIONFORMAT);
|
|
|
|
fDI->fActionFormat->dwActionSize = sizeof(DIACTION);
|
|
|
|
fDI->fActionFormat->dwDataSize = NUM_ACTIONS * sizeof(DWORD);
|
|
|
|
fDI->fActionFormat->dwNumActions = NUM_ACTIONS;
|
|
|
|
fDI->fActionFormat->guidActionMap = PL_ACTION_GUID;
|
|
|
|
fDI->fActionFormat->dwGenre = DIVIRTUAL_FIGHTING_THIRDPERSON;
|
|
|
|
fDI->fActionFormat->rgoAction = fActionMap;
|
|
|
|
fDI->fActionFormat->dwBufferSize = 16;
|
|
|
|
fDI->fActionFormat->lAxisMin = -1000;
|
|
|
|
fDI->fActionFormat->lAxisMax = 1000;
|
|
|
|
sprintf( fDI->fActionFormat->tszActionMap, "Plasma 2.0 Game Actions" );
|
|
|
|
|
|
|
|
// this call should not work:
|
|
|
|
fDI->fDInput->EnumDevices(DI8DEVCLASS_GAMECTRL, fPtr, fDI, DIEDFL_ATTACHEDONLY);
|
|
|
|
|
|
|
|
// apply the mapping to the game controller
|
|
|
|
// this is the correct <but broken> way to apply the action map:
|
|
|
|
// Pfunc3 fPtr3 = &plDInputMgr::EnumSuitableDevices;
|
|
|
|
// hr = fDI->fDInput->EnumDevicesBySemantics(NULL, fDI->fActionFormat, EnumSuitableDevices, fDI, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < fDI->fSticks.Count(); i++)
|
|
|
|
{
|
|
|
|
fDI->fSticks[i]->fCaps = TRACKED_NEW DIDEVCAPS;
|
|
|
|
fDI->fSticks[i]->fCaps->dwSize = sizeof(DIDEVCAPS);
|
|
|
|
hr = fDI->fSticks[i]->fDevice->GetCapabilities(fDI->fSticks[i]->fCaps);
|
|
|
|
hsAssert(!hr, "Unable to acquire devcaps in DInput Device!");
|
|
|
|
hr = fDI->fSticks[i]->fDevice->Acquire();
|
|
|
|
hsAssert(!hr, "Unable to acquire DInput Device!");
|
|
|
|
}
|
|
|
|
|
|
|
|
fhWnd = hWnd;
|
|
|
|
|
|
|
|
for (i = 0; i < fDI->fSticks.Count(); i++)
|
|
|
|
fInputDevice.Append( TRACKED_NEW plDInputDevice );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plDInputMgr::Update()
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!fDI->fSticks.Count())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Poll the devices to read the current state
|
|
|
|
for (int i = 0; i < fDI->fSticks.Count(); i++)
|
|
|
|
{
|
|
|
|
hr = fDI->fSticks[i]->fDevice->Poll();
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
// Attempt to reacquire joystick
|
|
|
|
while(hr == DIERR_INPUTLOST)
|
|
|
|
{
|
|
|
|
hr = fDI->fSticks[i]->fDevice->Acquire();
|
|
|
|
char str[256];
|
|
|
|
sprintf(str, "DInput Device # %d connection lost - press Ignore to attempt to reacquire!", i);
|
|
|
|
hsAssert(!hr, str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DIDEVICEOBJECTDATA data;
|
|
|
|
ULONG size = 1;
|
|
|
|
hr = fDI->fSticks[i]->fDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),&data,&size,0);
|
|
|
|
|
|
|
|
fInputDevice[i]->Update(&data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plDInputMgr::AddDevice(IDirectInputDevice8* device)
|
|
|
|
{
|
|
|
|
HRESULT hr = device->BuildActionMap(fDI->fActionFormat, NULL, NULL);
|
|
|
|
if (!FAILED(hr))
|
|
|
|
device->SetActionMap( fDI->fActionFormat, NULL, NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plDInputMgr::ConfigureDevice()
|
|
|
|
{
|
|
|
|
::ShowCursor( TRUE );
|
|
|
|
ReleaseCapture();
|
|
|
|
|
|
|
|
|
|
|
|
DICOLORSET dics;
|
|
|
|
ZeroMemory(&dics, sizeof(DICOLORSET));
|
|
|
|
dics.dwSize = sizeof(DICOLORSET);
|
|
|
|
|
|
|
|
DICONFIGUREDEVICESPARAMS dicdp;
|
|
|
|
ZeroMemory(&dicdp, sizeof(dicdp));
|
|
|
|
dicdp.dwSize = sizeof(dicdp);
|
|
|
|
dicdp.dwcUsers = 1;
|
|
|
|
dicdp.lptszUserNames = NULL;
|
|
|
|
|
|
|
|
dicdp.dwcFormats = 1;
|
|
|
|
dicdp.lprgFormats = fDI->fActionFormat;
|
|
|
|
dicdp.hwnd = fhWnd;
|
|
|
|
dicdp.lpUnkDDSTarget = NULL;
|
|
|
|
|
|
|
|
fDI->fDInput->ConfigureDevices(NULL, &dicdp, DICD_EDIT, NULL);
|
|
|
|
for (int i = 0; i < fDI->fSticks.Count(); i++)
|
|
|
|
fDI->fSticks[i]->fDevice->SetActionMap( fDI->fActionFormat, NULL, DIDSAM_FORCESAVE );
|
|
|
|
|
|
|
|
::ShowCursor( FALSE );
|
|
|
|
SetCapture(fhWnd);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plDInputMgr::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
plInputEventMsg* pMsg = plInputEventMsg::ConvertNoRef(msg);
|
|
|
|
if (pMsg && pMsg->fEvent == plInputEventMsg::kConfigure)
|
|
|
|
{
|
|
|
|
ConfigureDevice();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dinput required callback functions:
|
|
|
|
|
|
|
|
// enumerate the dinput devices
|
|
|
|
int __stdcall plDInputMgr::EnumGamepadCallback(const DIDEVICEINSTANCE* device, void* pRef)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
plDInput* pDI = (plDInput*)pRef;
|
|
|
|
IDirectInputDevice8* fStick = nil;
|
|
|
|
hr = pDI->fDInput->CreateDevice(device->guidInstance, &fStick, NULL);
|
|
|
|
|
|
|
|
if(!FAILED(hr))
|
|
|
|
{
|
|
|
|
pDI->fSticks.Append(TRACKED_NEW plDIDevice(fStick));
|
|
|
|
|
|
|
|
// the following code pertaining to the action map shouldn't be here.
|
|
|
|
// in fact this shouldn't work at all according to MS, but this is
|
|
|
|
// currently the only way this works. Whatever - the correct
|
|
|
|
// code is here and commented out in case this ever gets fixed by MS
|
|
|
|
// in a future release of dinput.
|
|
|
|
HRESULT hr = fStick->BuildActionMap(pDI->fActionFormat, NULL, NULL);
|
|
|
|
if (!FAILED(hr))
|
|
|
|
{
|
|
|
|
hr = fStick->SetActionMap( pDI->fActionFormat, NULL, NULL );
|
|
|
|
|
|
|
|
DIPROPDWORD dipW;
|
|
|
|
dipW.diph.dwSize = sizeof(DIPROPDWORD);
|
|
|
|
dipW.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
|
|
dipW.diph.dwHow = DIPH_DEVICE;
|
|
|
|
dipW.diph.dwObj = 0;
|
|
|
|
dipW.dwData = 500; // 5% of axis range for deadzone
|
|
|
|
|
|
|
|
hr = fStick->SetProperty(DIPROP_DEADZONE , &dipW.diph);
|
|
|
|
}
|
|
|
|
return DIENUM_CONTINUE;
|
|
|
|
}
|
|
|
|
return DIENUM_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
// look for axes on the controller and set the output range to +-100
|
|
|
|
// apparently not needed with action mapping:
|
|
|
|
/*
|
|
|
|
int __stdcall plDInputMgr::SetAxisRange(const DIDEVICEOBJECTINSTANCE* obj, void* pRef)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
DIPROPRANGE diprg;
|
|
|
|
|
|
|
|
diprg.diph.dwSize = sizeof(DIPROPRANGE);
|
|
|
|
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
|
|
diprg.diph.dwHow = DIPH_BYID;
|
|
|
|
diprg.diph.dwObj = obj->dwType;
|
|
|
|
diprg.lMin = -100;
|
|
|
|
diprg.lMax = +100;
|
|
|
|
|
|
|
|
plDInput* pDI = (plDInput*)pRef;
|
|
|
|
for (int i = 0; i < pDI->fSticks.Count(); i++)
|
|
|
|
hr = pDI->fSticks[i]->fDevice->SetProperty(DIPROP_RANGE, &diprg.diph);
|
|
|
|
|
|
|
|
if(!FAILED(hr))
|
|
|
|
return DIENUM_CONTINUE;
|
|
|
|
|
|
|
|
return DIENUM_STOP;
|
|
|
|
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// apply mapping to controller
|
|
|
|
// not used. why? no one really knows.
|
|
|
|
// leave this here in case dinput ever gets fixed...
|
|
|
|
/*
|
|
|
|
int __stdcall plDInputMgr::EnumSuitableDevices(const struct DIDEVICEINSTANCEA* devInst, struct IDirectInputDevice8* dev, unsigned long why, unsigned long devRemaining, void* pRef)
|
|
|
|
{
|
|
|
|
plDInput* pDI = (plDInput*)pRef;
|
|
|
|
HRESULT hr = dev->BuildActionMap(pDI->fActionFormat, NULL, NULL);
|
|
|
|
if (!FAILED(hr))
|
|
|
|
{
|
|
|
|
hr = dev->SetActionMap( pDI->fActionFormat, NULL, NULL );
|
|
|
|
}
|
|
|
|
return DIENUM_STOP;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
DIACTION plDInputMgr::fActionMap[NUM_ACTIONS] =
|
|
|
|
{
|
|
|
|
{A_CONTROL_MOVE, DIAXIS_TPS_MOVE, 0, "Walk Forward-Backward" ,},
|
|
|
|
{A_CONTROL_TURN, DIAXIS_TPS_TURN, 0, "Turn Left-Right" ,},
|
|
|
|
{A_CONTROL_MOUSE_X, DIAXIS_ANY_1, 0, "Move Camera Left-Right",},
|
|
|
|
{A_CONTROL_MOUSE_Y, DIAXIS_ANY_2, 0, "Move Camera Up-Down" ,},
|
|
|
|
{B_CONTROL_ACTION, DIBUTTON_TPS_ACTION, 0, "Action" ,},
|
|
|
|
{B_CONTROL_JUMP, DIBUTTON_TPS_JUMP, 0, "Jump" ,},
|
|
|
|
{B_CONTROL_STRAFE_LEFT, DIBUTTON_TPS_STEPLEFT, 0, "Strafe Left" ,},
|
|
|
|
{B_CONTROL_STRAFE_RIGHT, DIBUTTON_TPS_STEPRIGHT, 0, "Strafe Right" ,},
|
|
|
|
{B_CONTROL_MODIFIER_FAST, DIBUTTON_TPS_RUN, 0, "Run" ,},
|
|
|
|
{B_CONTROL_EQUIP, DIBUTTON_TPS_SELECT, 0, "Equip Item" ,},
|
|
|
|
{B_CONTROL_DROP, DIBUTTON_TPS_USE, 0, "Drop Item" ,},
|
|
|
|
{B_CONTROL_MOVE_FORWARD, DIBUTTON_ANY(0), 0, "Walk Forward" ,},
|
|
|
|
{B_CONTROL_MOVE_BACKWARD, DIBUTTON_ANY(1), 0, "Walk Backward" ,},
|
|
|
|
{B_CONTROL_ROTATE_LEFT, DIBUTTON_ANY(2), 0, "Turn Left" ,},
|
|
|
|
{B_CONTROL_ROTATE_RIGHT, DIBUTTON_ANY(3), 0, "Turn Right" ,},
|
|
|
|
{B_CONTROL_TURN_TO, DIBUTTON_ANY(4), 0, "Pick Item" ,},
|
|
|
|
{B_CAMERA_RECENTER, DIBUTTON_ANY(5), 0, "Recenter Camera" ,},
|
|
|
|
};
|