1
0
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-20 04:09:16 +00:00

Initial Commit of CyanWorlds.com Engine Open Source Client/Plugin

This commit is contained in:
JWPlatt
2011-03-12 12:34:52 -05:00
commit a20a222fc2
3976 changed files with 1301355 additions and 0 deletions

View File

@ -0,0 +1,252 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAvatarInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef plAvatarInputInterface_inc
#define plAvatarInputInterface_inc
#include "plInputInterface.h"
#include "../pnInputCore/plInputMap.h"
#include "hsTemplates.h"
#include "hsGeometry3.h"
#include "hsUtils.h"
//// Class Definition ////////////////////////////////////////////////////////
class plInputEventMsg;
class plMouseEventMsg;
class plKeyMap;
class plMouseMap;
class plKey;
class hsStream;
class hsResMgr;
class plAvatarInputMap;
class plPipeline;
class plAvatarInputInterface;
//// Little Input Map Helpers ////////////////////////////////////////////////
class plAvatarInputMap
{
protected:
plAvatarInputInterface *fInterface;
public:
plAvatarInputMap();
virtual ~plAvatarInputMap();
virtual char *GetName() = 0;
virtual hsBool IsBasic() { return false; }
plMouseMap *fMouseMap;
UInt32 fButtonState;
};
// Basic avatar mappings, for when the avatar is in "suspended input" mode.
class plSuspendedMovementMap : public plAvatarInputMap
{
public:
plSuspendedMovementMap();
virtual char *GetName() { return "Suspended Movement"; }
};
// The above, plus movement
class plBasicControlMap : public plSuspendedMovementMap
{
public:
plBasicControlMap();
virtual char *GetName() { return "Basic"; }
virtual hsBool IsBasic() { return true; }
};
// The above, plus movement
class plBasicThirdPersonControlMap : public plBasicControlMap
{
public:
plBasicThirdPersonControlMap();
virtual char *GetName() { return "Basic Third Person"; }
};
class plLadderControlMap : public plSuspendedMovementMap
{
public:
plLadderControlMap();
virtual char *GetName() { return "LadderClimb"; }
};
class plLadderMountMap : public plSuspendedMovementMap
{
public:
plLadderMountMap();
virtual char *GetName() { return "Ladder Mount"; }
};
class plLadderDismountMap : public plSuspendedMovementMap
{
public:
plLadderDismountMap();
virtual char *GetName() { return "Ladder Dismount"; }
};
class plBasicFirstPersonControlMap : public plBasicControlMap
{
public:
plBasicFirstPersonControlMap();
virtual char *GetName() { return "Basic First Person"; }
};
// Mouse walk mode
class pl3rdWalkMap : public plAvatarInputMap
{
public:
pl3rdWalkMap();
virtual ~pl3rdWalkMap();
};
class pl3rdWalkForwardMap : public pl3rdWalkMap
{
public:
pl3rdWalkForwardMap();
virtual char *GetName() { return "Walking Forward"; }
};
class pl3rdWalkBackwardMap : public pl3rdWalkMap
{
public:
pl3rdWalkBackwardMap();
virtual char *GetName() { return "Walking Backward"; }
};
class pl3rdWalkBackwardLBMap : public pl3rdWalkMap
{
public:
pl3rdWalkBackwardLBMap();
virtual char *GetName() { return "Walking Backward (LB)"; }
};
///////////////////////////////////////////////////////////////////////////////////
class plAvatarInputInterface : public plInputInterface
{
protected:
UInt32 fCurrentCursor;
hsScalar fCursorOpacity, fCursorTimeout, fCursorFadeDelay;
plAvatarInputMap *fInputMap;
static plAvatarInputInterface *fInstance;
virtual hsBool IHandleCtrlCmd( plCtrlCmd *cmd );
// Gets called once per IUpdate(), just like normal IEval()s
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty );
void IDeactivateCommand(plMouseInfo *info);
void IChangeInputMaps(plAvatarInputMap *newMap);
void ISetSuspendMovementMode();
void ISetBasicMode();
void ISetMouseWalkMode(ControlEventCode code);
void ISetLadderMap();
void ISetPreLadderMap();
void ISetPostLadderMap();
hsBool IHasControlFlag(int f) const { return fControlFlags.IsBitSet(f); }
void IClearControlFlag(int which) { fControlFlags.ClearBit( which ); }
hsBool CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box);
void ClearMouseCursor();
void DisableMouseInput() { fMouseDisabled = true; }
void EnableMouseInput() { fMouseDisabled = false; }
void Reset();
void RequestCursorToWorldPos(hsScalar xPos, hsScalar yPos, int ID);
hsBitVector fControlFlags;
hsBool fMouseDisabled;
plPipeline* fPipe;
int fCursorState;
int fCursorPriority;
hsBool f3rdPerson;
public:
plAvatarInputInterface();
virtual ~plAvatarInputInterface();
void CameraInThirdPerson(hsBool state);
// Always return true, since the cursor should be representing how we control the avatar
virtual hsBool HasInterestingCursorID( void ) const { return true; }
virtual UInt32 GetPriorityLevel( void ) const { return kAvatarInputPriority; }
virtual UInt32 GetCurrentCursorID( void ) const { return fCurrentCursor; }
virtual hsScalar GetCurrentCursorOpacity( void ) const { return fCursorOpacity; }
char* GetInputMapName() { return fInputMap ? fInputMap->GetName() : ""; }
virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg );
virtual void MissedInputEvent( plInputEventMsg *pMsg );
virtual hsBool MsgReceive( plMessage *msg );
virtual void Init( plInputInterfaceMgr *manager );
virtual void Shutdown( void );
virtual void RestoreDefaultKeyMappings( void );
virtual void ClearKeyMap();
// [dis/en]able mouse commands for avatar movement
void SuspendMouseMovement();
void EnableMouseMovement();
void EnableJump(hsBool val);
void EnableForwardMovement(hsBool val);
void EnableControl(hsBool val, ControlEventCode code);
void ClearLadderMode();
void SetLadderMode();
void ForceAlwaysRun(hsBool val);
void SetControlFlag(int f, hsBool val = true) { fControlFlags.SetBit(f, val); }
void SetCursorFadeDelay( hsScalar delay ) { fCursorFadeDelay = delay; }
hsBool IsEnterChatModeBound();
static plAvatarInputInterface *GetInstance( void ) { return fInstance; }
};
#endif plAvatarInputInterface_inc

View File

@ -0,0 +1,277 @@
/*==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/>.
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==*/
// plDInputDevice.cpp
#include "hsConfig.h"
#include "hsWindows.h"
#include "plDInputDevice.h"
#include "plgDispatch.h"
#include "../plMessage/plInputEventMsg.h"
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
//
//
//
// plDInputDevice
//
//
plDInputDevice::plDInputDevice() :
fX(0.5),
fY(0.5)
{
}
plDInputDevice::~plDInputDevice()
{
}
void plDInputDevice::Update(DIDEVICEOBJECTDATA* js)
{
switch(js->uAppData)
{
case A_CONTROL_MOVE:
{
int i = (int)(js->dwData);
if (i <= -1)
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_MOVE_FORWARD );
pMsg->SetControlActivated( true );
plgDispatch::MsgSend( pMsg );
}
else
if (i >= 1)
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_MOVE_BACKWARD );
pMsg->SetControlActivated( true );
plgDispatch::MsgSend( pMsg );
}
else
if (i == 0)
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_MOVE_BACKWARD );
pMsg->SetControlActivated( false );
plgDispatch::MsgSend( pMsg );
plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg;
pMsg2->SetControlCode( B_CONTROL_MOVE_FORWARD );
pMsg2->SetControlActivated( false );
plgDispatch::MsgSend( pMsg2 );
}
}
break;
case A_CONTROL_TURN:
{
int i = (int)(js->dwData);
float f = ((float)i) * 0.001f;
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( A_CONTROL_TURN );
if (f <= 0.02 && f >= -0.02)
pMsg->SetControlActivated( false );
else
pMsg->SetControlActivated( true );
pMsg->SetControlPct(f);
plgDispatch::MsgSend( pMsg );
}
break;
case B_CONTROL_ACTION:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_ACTION );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_MODIFIER_FAST:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_MODIFIER_FAST );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_JUMP:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_JUMP );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_STRAFE_LEFT:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_STRAFE_LEFT );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_STRAFE_RIGHT:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_STRAFE_RIGHT);
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_EQUIP:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_EQUIP );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_DROP:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_DROP );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_MOVE_FORWARD:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_MOVE_FORWARD );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_MOVE_BACKWARD:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_MOVE_BACKWARD );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_ROTATE_RIGHT:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_ROTATE_RIGHT);
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_ROTATE_LEFT:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_ROTATE_LEFT );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CONTROL_TURN_TO:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CONTROL_TURN_TO );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case B_CAMERA_RECENTER:
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlCode( B_CAMERA_RECENTER );
pMsg->SetControlActivated(js->dwData & 0x80);
plgDispatch::MsgSend(pMsg);
}
break;
case A_CONTROL_MOUSE_X:
{
int i = (int)(js->dwData);
float f = ((float)i) * 0.001f;
if (f <= 0.02 && f >= -0.02)
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlActivated( false );
pMsg->SetControlCode(B_CAMERA_ROTATE_DOWN);
pMsg->SetControlPct(0);
plgDispatch::MsgSend( pMsg );
plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg;
pMsg2->SetControlActivated( false );
pMsg2->SetControlCode(B_CAMERA_ROTATE_UP);
pMsg2->SetControlPct(0);
plgDispatch::MsgSend( pMsg2 );
}
else
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlActivated( true );
if (f < 0)
pMsg->SetControlCode(B_CAMERA_ROTATE_DOWN);
else
pMsg->SetControlCode(B_CAMERA_ROTATE_UP);
pMsg->SetControlPct(f);
plgDispatch::MsgSend( pMsg );
}
}
break;
case A_CONTROL_MOUSE_Y:
{
int i = (int)(js->dwData);
float f = ((float)i) * 0.001f;
if (f <= 0.02 && f >= -0.02)
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlActivated( false );
pMsg->SetControlCode(B_CAMERA_ROTATE_RIGHT);
pMsg->SetControlPct(0);
plgDispatch::MsgSend( pMsg );
plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg;
pMsg2->SetControlActivated( false );
pMsg2->SetControlCode(B_CAMERA_ROTATE_LEFT);
pMsg2->SetControlPct(0);
plgDispatch::MsgSend( pMsg2 );
}
else
{
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
pMsg->SetControlActivated( true );
if (f < 0)
pMsg->SetControlCode(B_CAMERA_ROTATE_RIGHT);
else
pMsg->SetControlCode(B_CAMERA_ROTATE_LEFT);
pMsg->SetControlPct(f);
plgDispatch::MsgSend( pMsg );
}
}
break;
default:
break;
}
}

View File

@ -0,0 +1,50 @@
/*==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/>.
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==*/
// plDInputDevice.h
#ifndef PL_DINPUT_DEVICE_H
#define PL_DINPUT_DEVICE_H
#include "plInputDevice.h"
struct DIDEVICEOBJECTDATA;
class plDInputDevice : public plInputDevice
{
public:
plDInputDevice();
~plDInputDevice();
const char* GetInputName() { return "DInput"; }
virtual void Update(DIDEVICEOBJECTDATA* js);
protected:
hsScalar fX,fY;
};
#endif // PL_INPUT_DEVICE_H

View File

@ -0,0 +1,341 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plDebugInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsConfig.h"
#include "hsWindows.h"
#include "hsTypes.h"
#include "plDebugInputInterface.h"
#include "plInputInterfaceMgr.h"
#include "plInputManager.h"
#include "plInputDevice.h"
#include "../plMessage/plInputIfaceMgrMsg.h"
#include "../plMessage/plInputEventMsg.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnInputCore/plKeyMap.h"
#include "plgDispatch.h"
#include "plPipeline.h"
#include "hsConfig.h"
plDebugInputInterface *plDebugInputInterface::fInstance = nil;
//// Constructor/Destructor //////////////////////////////////////////////////
plDebugInputInterface::plDebugInputInterface()
{
fInstance = this;
// Add our control codes to our control map. Do NOT add the key bindings yet.
// Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth.
// This part basically declares us master of the bindings for these commands.
// IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO
// RestoreDefaultKeyMappings()!!!!
#ifndef PLASMA_EXTERNAL_RELEASE
// fControlMap->AddCode( B_CONTROL_MODIFIER_FAST, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_DRIVE_SPEED_UP, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_DRIVE_SPEED_DOWN, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_MOVE_FORWARD, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_MOVE_BACKWARD, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_MOVE_LEFT, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_MOVE_RIGHT, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_MOVE_UP, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_MOVE_DOWN, kControlFlagNormal | kControlFlagNoRepeat );
// fControlMap->AddCode( B_TOGGLE_DRIVE_MODE, kControlFlagNormal | kControlFlagNoRepeat | kControlFlagShift );
#endif
// IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO
// RestoreDefaultKeyMappings()!!!!
}
plDebugInputInterface::~plDebugInputInterface()
{
fInstance = nil;
}
//// Init/Shutdown ///////////////////////////////////////////////////////////
void plDebugInputInterface::Init( plInputInterfaceMgr *manager )
{
plInputInterface::Init( manager );
}
void plDebugInputInterface::Shutdown( void )
{
}
//// RestoreDefaultKeyMappings ///////////////////////////////////////////////
void plDebugInputInterface::RestoreDefaultKeyMappings( void )
{
if( fControlMap == nil )
return;
fControlMap->UnmapAllBindings();
#ifndef PLASMA_EXTERNAL_RELEASE
// fControlMap->BindKey( KEY_SHIFT, B_CONTROL_MODIFIER_FAST );
fControlMap->BindKey( plShiftKeyCombo( KEY_EQUAL ), B_CAMERA_DRIVE_SPEED_UP );
fControlMap->BindKey( plShiftKeyCombo( KEY_DASH ), B_CAMERA_DRIVE_SPEED_DOWN );
fControlMap->BindKey( KEY_W, B_CAMERA_MOVE_FORWARD );
fControlMap->BindKey( KEY_S, B_CAMERA_MOVE_BACKWARD );
fControlMap->BindKey( KEY_A, B_CAMERA_MOVE_LEFT );
fControlMap->BindKey( KEY_D, B_CAMERA_MOVE_RIGHT );
fControlMap->BindKey( KEY_I, B_CAMERA_MOVE_UP );
fControlMap->BindKey( KEY_K, B_CAMERA_MOVE_DOWN );
// fControlMap->BindKey( KEY_C, B_TOGGLE_DRIVE_MODE );
#endif
}
//// IEval ///////////////////////////////////////////////////////////////////
hsBool plDebugInputInterface::IEval( double secs, hsScalar del, UInt32 dirty )
{
return true;
}
//// MsgReceive //////////////////////////////////////////////////////////////
hsBool plDebugInputInterface::MsgReceive( plMessage *msg )
{
return plInputInterface::MsgReceive(msg);
}
//// cursorinbox /////////////////////////////////////////////////////
hsBool plDebugInputInterface::CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box)
{
return ( pMsg->GetXPos() >= box.fX && pMsg->GetXPos() <= box.fY && pMsg->GetYPos() >= box.fZ && pMsg->GetYPos() <= box.fW );
}
//// InterpretInputEvent /////////////////////////////////////////////////////
hsBool plDebugInputInterface::InterpretInputEvent( plInputEventMsg *pMsg )
{
hsBool handled = false;
plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(pMsg);
if (pMouseMsg)
{
// check for button presses...
if (fButtonState & kLeftButtonDown)
{
fButtonState |= kLeftButtonRepeat;
}
if (fButtonState & kRightButtonDown)
{
fButtonState |= kRightButtonRepeat;
}
if (pMouseMsg->GetButton() == kLeftButtonDown)
{
fButtonState |= kLeftButtonDown;
}
if (pMouseMsg->GetButton() == kLeftButtonUp)
{
fButtonState &= ~kLeftButtonDown;
fButtonState &= ~kLeftButtonRepeat;
}
if (pMouseMsg->GetButton() == kRightButtonDown)
{
fButtonState |= kRightButtonDown;
}
if (pMouseMsg->GetButton() == kRightButtonUp)
{
fButtonState &= ~kRightButtonDown;
fButtonState &= ~kRightButtonRepeat;
}
for (int i=0; i < fMouseMap.fMap.Count(); i++)
{
// is this control already set?
if (fControlFlags.IsBitSet(fMouseMap.fMap[i]->fCode))
{
// can we disable this control?
hsBool disable = false;
// can we disable this control based on a button?
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButton && !(fButtonState & kLeftButtonDown))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButton && !(fButtonState & kRightButtonDown))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && (fButtonState & kLeftButtonRepeat))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && (fButtonState & kRightButtonRepeat))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && !(fButtonState & kLeftButtonDown))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && !(fButtonState & kRightButtonDown))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonRepeat && !(fButtonState & kLeftButtonDown))
disable = true;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonRepeat && !(fButtonState & kRightButtonDown))
disable = true;
// can we disable this control based on the cursor position?
if (!CursorInBox(pMouseMsg, fMouseMap.fMap[i]->fBox) && fMouseMap.fMap[i]->fControlFlags & kControlFlagBoxDisable)
disable = true;
if (disable)
{
plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this );
pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate;
pCmd->fControlActivated = false;
pCmd->fControlCode = fMouseMap.fMap[i]->fCode;
fControlFlags.ClearBit(pCmd->fControlCode);
fMessageQueue->Append(pCmd);
handled = true;
continue;
}
// is it a range control? If so we need to re-send the command
if ((fMouseMap.fMap[i]->fControlFlags & kControlFlagRangePos) || (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangeNeg))
{
plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this );
pCmd->fControlActivated = true;
pCmd->fControlCode = fMouseMap.fMap[i]->fCode;
hsScalar pct = 0.0f;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangePos)
{
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent)
pct = hsABS((fMouseMap.fMap[i]->fBox.fX - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX));
else
pct = hsABS((fMouseMap.fMap[i]->fBox.fZ - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ));
}
else
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangeNeg)
{
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent)
pct = hsABS((fMouseMap.fMap[i]->fBox.fY - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX));
else
pct = hsABS((fMouseMap.fMap[i]->fBox.fW - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ));
}
pCmd->fPct = pct;
if (pct == 1.0f || pct == -1.0f)
{
delete pCmd;
break;
}
pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate;
fMessageQueue->Append(pCmd);
}
}
else // if it is an 'always if in box' command see if it's not in the box
if ( (fMouseMap.fMap[i]->fControlFlags & kControlFlagInBox) && (!CursorInBox(pMouseMsg, fMouseMap.fMap[i]->fBox)) )
{
plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this );
pCmd->fControlActivated = false;
pCmd->fControlCode = fMouseMap.fMap[i]->fCode;
pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate;
fMessageQueue->Append(pCmd);
continue;
}
else // the control is not set, see if we should set it.
{
// is the cursor in the appropriate box?
if (CursorInBox(pMouseMsg, fMouseMap.fMap[i]->fBox))
{
// do we require a button?
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButton && !(fButtonState & kLeftButtonDown))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButton && !(fButtonState & kRightButtonDown))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && (fButtonState & kLeftButtonRepeat))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && (fButtonState & kRightButtonRepeat))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonRepeat && !(fButtonState & kLeftButtonRepeat))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonRepeat && !(fButtonState & kRightButtonRepeat))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && !(fButtonState & kLeftButtonDown))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && !(fButtonState & kLeftButtonDown))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonUp && !(pMouseMsg->GetButton() == kLeftButtonUp))
continue;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonUp && !(pMouseMsg->GetButton() == kRightButtonUp))
continue;
// okay, we're in the box and either we don't require a button or our button is pressed.
// so set the command as 'enabled'
// UNLESS it has kControlFlagInBox, which means we want it sent every frame it is in the box
if (!(fMouseMap.fMap[i]->fControlFlags & kControlFlagInBox))
fControlFlags.SetBit(fMouseMap.fMap[i]->fCode);
// issue the command
plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this );
pCmd->fControlActivated = true;
pCmd->fControlCode = fMouseMap.fMap[i]->fCode;
pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate;
// figure out what percent (if any)
hsScalar pct = 0.0f;
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangePos)
{
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent)
pct = hsABS((fMouseMap.fMap[i]->fBox.fX - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX));
else
pct = hsABS((fMouseMap.fMap[i]->fBox.fZ - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ));
}
else
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangeNeg)
{
if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent)
pct = hsABS((fMouseMap.fMap[i]->fBox.fY - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX));
else
pct = hsABS((fMouseMap.fMap[i]->fBox.fW - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ));
}
pCmd->fPct = pct;
if (pct == 1.0f || pct == -1.0f)
{
delete pCmd;
break;
}
// and add it to the list
fMessageQueue->Append(pCmd);
handled = true;
continue;
}
}
}
return handled;
}
return false;
}

View File

@ -0,0 +1,78 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plDebugInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plDebugInputInterface_h
#define _plDebugInputInterface_h
#include "plInputInterface.h"
#include "../pnInputCore/plInputMap.h"
//// Class Definition ////////////////////////////////////////////////////////
class plMouseEventMsg;
class plDebugInputInterface : public plInputInterface
{
protected:
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty );
hsBool CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box);
plMouseMap fMouseMap;
UInt32 fButtonState;
hsBitVector fControlFlags;
static plDebugInputInterface *fInstance;
public:
plDebugInputInterface();
virtual ~plDebugInputInterface();
// Always return false,
virtual hsBool HasInterestingCursorID( void ) const { return false; }
virtual UInt32 GetPriorityLevel( void ) const { return kDebugCmdPrioity; }
virtual void RestoreDefaultKeyMappings( void );
virtual UInt32 GetCurrentCursorID( void ) const { return 0; }
virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg );
virtual hsBool MsgReceive( plMessage *msg );
virtual void Init( plInputInterfaceMgr *manager );
virtual void Shutdown( void );
static plDebugInputInterface *GetInstance( void ) { return fInstance; }
};
#endif //_plDebugInputInterface_h

View File

@ -0,0 +1,40 @@
/*==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/>.
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==*/
#ifndef plInputCoreCreatable_inc
#define plInputCoreCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plInputManager.h"
REGISTER_CREATABLE(plInputManager);
#include "plInputInterfaceMgr.h"
REGISTER_CREATABLE(plInputInterfaceMgr);
#endif // plInputCoreCreatable_inc

View File

@ -0,0 +1,944 @@
/*==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/>.
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==*/
// plInputDevice.cpp
//#include "STRING"
#include "hsConfig.h"
#include "hsWindows.h"
#include "plInputDevice.h"
#include "plInputManager.h"
#include "plAvatarInputInterface.h"
#include "../plMessage/plInputEventMsg.h"
#include "../pnMessage/plTimeMsg.h"
#include "hsUtils.h"
#include "plgDispatch.h"
#include "../plPipeline/plPlates.h"
#include "../plPipeline/plDebugText.h"
#include "hsWindows.h"
#include "../NucleusLib/inc/plPipeline.h"
// base size of the cursor
#define CURSOR_SIZE_X 0.0675f
#define CURSOR_SIZE_Y 0.09f
// The resolution that uses the base size of the cursor.
// All other resolutions will scale the cursor size to keep the same physical size.
#define BASE_WIDTH 1024
#define BASE_HEIGHT 768
plKeyboardDevice* plKeyboardDevice::fInstance = nil;
bool plKeyboardDevice::fKeyboardState[256];
hsBool plKeyboardDevice::fIgnoreCapsLock = false;
hsBool plKeyboardDevice::fKeyIsDeadKey = false;
plKeyboardDevice::plKeyboardDevice() :
fShiftKeyDown(false),
fCapsLockKeyDown(false),
fAltKeyDown(false),
fCtrlKeyDown(false),
fCapsLockLock(false),
fPrevNumLockOn(false),
fControlMode(STANDARD_MODE)
{
fInstance = this;
fStartedUpWithNumLockOn = ((GetKeyState(VK_NUMLOCK) & 1) != 0);
InitKeyboardState();
}
plKeyboardDevice::~plKeyboardDevice()
{
if (fStartedUpWithNumLockOn)
ForceNumLock(true);
}
void plKeyboardDevice::InitKeyboardState()
{
static bool initialized = false;
if (!initialized)
{
for (unsigned int i = 0; i < 256; ++i)
plKeyboardDevice::fKeyboardState[i] = false;
initialized = true;
}
}
void plKeyboardDevice::ReleaseAllKeys()
{
// send a key-up message for all "normal" keys
for (unsigned int i = 0; i < 256; ++i)
{
if ((i == KEY_SHIFT) || (i == KEY_CTRL) || (i == KEY_CAPSLOCK))
continue; // these are handled slightly differently
if (fKeyboardState[i])
{
fKeyboardState[i] = false;
// fake a key-up command
plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg;
pMsg->SetKeyCode( (plKeyDef)i );
pMsg->SetKeyDown( false );
pMsg->SetShiftKeyDown( fShiftKeyDown );
pMsg->SetCtrlKeyDown( fCtrlKeyDown );
pMsg->SetCapsLockKeyDown( fCapsLockLock );
pMsg->SetRepeat( false );
plgDispatch::MsgSend( pMsg );
}
}
// send key messages for shift and ctrl if necessary because the keys above need to have
// the proper states of the shift and ctrl keys sent with their messages, and our internal
// flags for these keys need to be cleared. We don't send a key-up message for caps lock
// because it doesn't really operate like every other key on the keyboard
if (fKeyboardState[KEY_SHIFT])
{
fKeyboardState[KEY_SHIFT] = false;
fShiftKeyDown = false;
plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg;
pMsg->SetKeyCode( KEY_SHIFT );
pMsg->SetKeyDown( false );
pMsg->SetShiftKeyDown( false );
pMsg->SetCtrlKeyDown( false );
pMsg->SetCapsLockKeyDown( fCapsLockLock );
pMsg->SetRepeat( false );
plgDispatch::MsgSend( pMsg );
}
if (fKeyboardState[KEY_CTRL])
{
fKeyboardState[KEY_CTRL] = false;
fCtrlKeyDown = false;
plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg;
pMsg->SetKeyCode( KEY_CTRL );
pMsg->SetKeyDown( false );
pMsg->SetShiftKeyDown( false );
pMsg->SetCtrlKeyDown( false );
pMsg->SetCapsLockKeyDown( fCapsLockLock );
pMsg->SetRepeat( false );
plgDispatch::MsgSend( pMsg );
}
}
hsBool plKeyboardDevice::IsCapsLockKeyOn()
{
return fCapsLockLock;
}
void plKeyboardDevice::Shutdown()
{
}
#if HS_BUILD_FOR_WIN32
void plKeyboardDevice::ForceNumLock(hsBool on)
{
if (on != ((GetKeyState(VK_NUMLOCK) & 1) != 0))
{
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&info);
if (info.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
keybd_event( VK_NUMLOCK, 0, KEYEVENTF_EXTENDEDKEY | 0, 0 );
keybd_event( VK_NUMLOCK, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
}
else
{
UInt8 keyState[256];
GetKeyboardState(keyState);
keyState[VK_NUMLOCK] = keyState[VK_NUMLOCK] ^ 1;
SetKeyboardState(keyState);
}
}
}
#endif //HS_BUILD_FOR_WIN32
void plKeyboardDevice::HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat)
{
// update the internal keyboard state
unsigned int keyCode = (unsigned int)key;
if ((key >= 0) && (key < 256))
fKeyboardState[key] = bKeyDown;
#if HS_BUILD_FOR_WIN32
if (key == VK_NUMLOCK && bKeyDown)
{
ForceNumLock(false);
}
#endif // HS_BUILD_FOR_WIN32
if (key == KEY_SHIFT)
{
fShiftKeyDown = bKeyDown;
// return;
}
if (key == KEY_CTRL)
{
fCtrlKeyDown = bKeyDown;
// return;
}
if (key == KEY_CAPSLOCK)
{
// Keyboards toggle the light on key-down, so I'm going with that.
if (bKeyDown && !bKeyRepeat)
{
fCapsLockLock = !fCapsLockLock;
plAvatarInputInterface::GetInstance()->ForceAlwaysRun(fCapsLockLock);
}
}
// send a key event...
plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg;
pMsg->SetKeyCode( key );
pMsg->SetKeyDown( bKeyDown );
pMsg->SetShiftKeyDown( fShiftKeyDown );
pMsg->SetCtrlKeyDown( fCtrlKeyDown );
pMsg->SetCapsLockKeyDown( fCapsLockLock );
pMsg->SetRepeat(bKeyRepeat);
plgDispatch::MsgSend( pMsg );
}
void plKeyboardDevice::HandleWindowActivate(bool bActive, HWND hWnd)
{
if (bActive)
{
fCtrlKeyDown = false;
#if HS_BUILD_FOR_WIN32
{
fPrevNumLockOn = ((GetKeyState(VK_NUMLOCK) & 1) != 0);
ForceNumLock(false);
hsBool oldLock = fCapsLockLock;
fCapsLockLock = (GetKeyState(KEY_CAPSLOCK) & 1) != 0;
if (fCapsLockLock != oldLock)
plAvatarInputInterface::GetInstance()->ForceAlwaysRun(fCapsLockLock);
}
#endif
}
else
{
ReleaseAllKeys(); // send key-up events for everything since we're losing focus
#if HS_BUILD_FOR_WIN32
{
if (fPrevNumLockOn)
ForceNumLock(true);
}
#endif
}
}
//// KeyEventToChar //////////////////////////////////////////////////////////
// Translate a Plasma key event to an actual char
char plKeyboardDevice::KeyEventToChar( plKeyEventMsg *msg )
{
short code = msg->GetKeyCode();
char c = 0;
unsigned char *kbState = TRACKED_NEW unsigned char[256];
unsigned char *buffer = TRACKED_NEW unsigned char[256];
UINT scanCode;
int retVal;
buffer[0] = 0;
switch( code )
{
case KEY_A:
case KEY_B:
case KEY_C:
case KEY_D:
case KEY_E:
case KEY_F:
case KEY_G:
case KEY_H:
case KEY_I:
case KEY_J:
case KEY_K:
case KEY_L:
case KEY_M:
case KEY_N:
case KEY_O:
case KEY_P:
case KEY_Q:
case KEY_R:
case KEY_S:
case KEY_T:
case KEY_U:
case KEY_V:
case KEY_W:
case KEY_X:
case KEY_Y:
case KEY_Z:
case KEY_1:
case KEY_2:
case KEY_3:
case KEY_4:
case KEY_5:
case KEY_6:
case KEY_7:
case KEY_8:
case KEY_9:
case KEY_0:
case KEY_TILDE:
case KEY_COMMA:
case KEY_PERIOD:
case KEY_LBRACKET:
case KEY_RBRACKET:
case KEY_BACKSLASH:
case KEY_SLASH:
case KEY_DASH:
case KEY_EQUAL:
case KEY_SEMICOLON:
case KEY_QUOTE:
// let windows translate everything for us!
scanCode = MapVirtualKeyEx(code,0,GetKeyboardLayout(0));
GetKeyboardState(kbState);
if (fIgnoreCapsLock)
kbState[KEY_CAPSLOCK] = 0; // clear the caps lock key
retVal = ToAsciiEx(code,scanCode,kbState,(unsigned short*)buffer,0,GetKeyboardLayout(0));
if (retVal == 2)
{
if ((buffer[0] == buffer[1]) && (!fKeyIsDeadKey))
{
// it's actually a dead key, since the previous key wasn't a dead key
c = (char)buffer[0];
fKeyIsDeadKey = true;
}
else
{
c = (char)buffer[1]; // it was an untranslated dead key, so copy the unconverted key
fKeyIsDeadKey = false;
}
}
else if (retVal == 0)
c = 0; // it's invalid
else
{
c = (char)buffer[0];
if (retVal < 0) // the key was a dead key
fKeyIsDeadKey = true;
else
fKeyIsDeadKey = false;
}
break;
case KEY_ESCAPE: c = 27; break;
case KEY_TAB: c = '\t'; break;
case KEY_BACKSPACE: c = 8; break;
case KEY_ENTER: c = '\n'; break;
case KEY_SPACE: c = ' '; break;
// numlock on numbers
case KEY_NUMPAD0: c = '0'; break;
case KEY_NUMPAD1: c = '1'; break;
case KEY_NUMPAD2: c = '2'; break;
case KEY_NUMPAD3: c = '3'; break;
case KEY_NUMPAD4: c = '4'; break;
case KEY_NUMPAD5: c = '5'; break;
case KEY_NUMPAD6: c = '6'; break;
case KEY_NUMPAD7: c = '7'; break;
case KEY_NUMPAD8: c = '8'; break;
case KEY_NUMPAD9: c = '9'; break;
// everything else
default:
c = 0;
break;
}
delete [] kbState;
delete [] buffer;
return c;
}
//
//
//
// plMouseDevice
//
//
bool plMouseDevice::bMsgAlways = true;
bool plMouseDevice::bCursorHidden = false;
bool plMouseDevice::bCursorOverride = false;
bool plMouseDevice::bInverted = false;
hsScalar plMouseDevice::fWidth = BASE_WIDTH;
hsScalar plMouseDevice::fHeight = BASE_HEIGHT;
plMouseDevice* plMouseDevice::fInstance = 0;
plMouseDevice::plMouseDevice()
{
fXPos = 0;
fYPos = 0;
fCursorID = CURSOR_UP;
fButtonState = 0;
fOpacity = 1.f;
fCursor = nil;
CreateCursor( fCursorID );
plMouseDevice::fInstance = this;
fXMsg = nil;
fYMsg = nil;
fB2Msg = nil;
fLeftBMsg[0] = nil;
fLeftBMsg[1] = nil;
fRightBMsg[0] = nil;
fRightBMsg[1] = nil;
fMiddleBMsg[0] = nil;
fMiddleBMsg[1] = nil;
}
plMouseDevice::~plMouseDevice()
{
plPlateManager::Instance().DestroyPlate( fCursor );
fCursor = nil;
plMouseDevice::fInstance = nil;
}
void plMouseDevice::SetDisplayResolution(hsScalar Width, hsScalar Height)
{
fWidth = Width;
fHeight = Height;
IUpdateCursorSize();
}
void plMouseDevice::CreateCursor( int cursor )
{
if( fCursor == nil )
{
plPlateManager::Instance().CreatePlate( &fCursor );
fCursor->CreateFromResource( MAKEINTRESOURCE( cursor ) );
}
else
{
fCursor->ReloadFromResource( MAKEINTRESOURCE( cursor ) );
}
fCursor->SetPosition( 0, 0, 0 );
IUpdateCursorSize();
fCursor->SetVisible( true );
fCursor->SetOpacity( fOpacity );
}
void plMouseDevice::IUpdateCursorSize()
{
if(fCursor)
{
// set the size of the cursor based on resolution.
fCursor->SetSize( CURSOR_SIZE_X * BASE_WIDTH / fWidth, CURSOR_SIZE_Y * BASE_HEIGHT / fHeight );
}
}
void plMouseDevice::AddNameToCursor(const char* name)
{
if (fInstance && name)
{
plDebugText &txt = plDebugText::Instance();
txt.DrawString(fInstance->fWXPos + 12 ,fInstance->fWYPos - 7,name);
}
}
void plMouseDevice::AddCCRToCursor()
{
if (fInstance)
{
plDebugText &txt = plDebugText::Instance();
txt.DrawString(fInstance->fWXPos + 12, fInstance->fWYPos - 17, "CCR");
}
}
void plMouseDevice::AddIDNumToCursor(UInt32 idNum)
{
if (fInstance && idNum)
{
plDebugText &txt = plDebugText::Instance();
char str[256];
sprintf(str, "%d",idNum);
txt.DrawString(fInstance->fWXPos + 12 ,fInstance->fWYPos + 3,str);
}
}
void plMouseDevice::SetCursorX(hsScalar x)
{
/// Set the cursor position
if( fCursor == nil && !plMouseDevice::bCursorHidden)
CreateCursor( fCursorID );
if (fCursor)
fCursor->SetPosition( ( x * 2.0f ) - 1.0f,
( fYPos * 2.0f ) - 1.0f );
// plDebugText &txt = plDebugText::Instance();
// txt.DrawString(fWXPos + 20,fWYPos - 5,"test");
}
void plMouseDevice::SetCursorY(hsScalar y)
{
/// Set the cursor position
if( fCursor == nil && !plMouseDevice::bCursorHidden)
CreateCursor( fCursorID );
if (fCursor)
fCursor->SetPosition( ( fXPos * 2.0f ) - 1.0f,
( y * 2.0f ) - 1.0f );
// plDebugText &txt = plDebugText::Instance();
// txt.DrawString(fWXPos + 20,fWYPos - 10,"test");
}
void plMouseDevice::HideCursor(hsBool override)
{
if( fInstance->fCursor != nil )
fInstance->fCursor->SetVisible( false );
plMouseDevice::bCursorOverride = (override != 0);
plMouseDevice::bCursorHidden = true;
}
void plMouseDevice::ShowCursor(hsBool override)
{
if( !plMouseDevice::bCursorHidden )
return;
if (plMouseDevice::bCursorOverride && !override)
return;
plMouseDevice::bCursorHidden = false;
plMouseDevice::bCursorOverride = false;
if( fInstance->fCursor == nil )
fInstance->CreateCursor( fInstance->fCursorID );
fInstance->fCursor->SetVisible( true );
}
void plMouseDevice::NewCursor(int cursor)
{
fInstance->fCursorID = cursor;
fInstance->CreateCursor(cursor);
fInstance->SetCursorX(fInstance->GetCursorX());
fInstance->SetCursorY(fInstance->GetCursorY());
if (!plMouseDevice::bCursorHidden)
fInstance->fCursor->SetVisible( true );
}
void plMouseDevice::SetCursorOpacity( hsScalar opacity )
{
fInstance->fOpacity = opacity;
if( fInstance->fCursor != nil )
fInstance->fCursor->SetOpacity( opacity );
}
hsBool plMouseDevice::MsgReceive(plMessage* msg)
{
plEvalMsg* pEMsg = plEvalMsg::ConvertNoRef(msg);
if (pEMsg)
{
if (fXMsg)
{
plgDispatch::MsgSend(fXMsg);
fXMsg = nil;
}
else
{
plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg;
pMsg->SetXPos( fXPos );
pMsg->SetYPos( fYPos );
pMsg->SetDX(0);
pMsg->SetDY(0);
plgDispatch::MsgSend(pMsg);
}
if (fYMsg)
{
plgDispatch::MsgSend(fYMsg);
fYMsg = nil;
}
else
{
plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg;
pMsg->SetXPos( fXPos );
pMsg->SetYPos( fYPos );
pMsg->SetDX(0);
pMsg->SetDY(0);
plgDispatch::MsgSend(pMsg);
}
if( fB2Msg )
{
fB2Msg->Send();
fB2Msg = nil;
}
// look for mouse button events in the queues to be sent now
// ...Left mouse button
if ( fLeftBMsg[0] != nil)
{
fLeftBMsg[0]->Send();
// slide queue elements over... get 'em on the next eval
fLeftBMsg[0] = fLeftBMsg[1];
fLeftBMsg[1] = nil;
}
// ...Right mouse button
if ( fRightBMsg[0] != nil)
{
fRightBMsg[0]->Send();
// slide queue elements over... get 'em on the next eval
fRightBMsg[0] = fRightBMsg[1];
fRightBMsg[1] = nil;
}
// ...middle mouse button
if ( fMiddleBMsg[0] != nil)
{
fMiddleBMsg[0]->Send();
// slide queue elements over... get 'em on the next eval
fMiddleBMsg[0] = fMiddleBMsg[1];
fMiddleBMsg[1] = nil;
}
}
plIMouseXEventMsg* pXMsg = plIMouseXEventMsg::ConvertNoRef(msg);
if (pXMsg)
{
// send a mouse event
plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg;
if (pXMsg->fX == 999)
pMsg->SetXPos( fXPos + 0.001f );
else
if (pXMsg->fX == -999)
pMsg->SetXPos( fXPos - 0.001f );
else
pMsg->SetXPos(pXMsg->fX);
pMsg->SetYPos( fYPos );
pMsg->SetDX( ( fXPos - pMsg->GetXPos()) );
pMsg->SetDY(0);
if (pMsg->GetDX() == 0.0f && !plMouseDevice::bMsgAlways)
{
delete pMsg;
return true;
}
if (fXMsg)
delete fXMsg;
fXMsg = pMsg;
if (pXMsg->fX == 999)
fXPos += 0.01;
else
if (pXMsg->fX == -999)
fXPos -= 0.01;
else
fXPos = pXMsg->fX;
SetCursorX(fXPos);
fWXPos = pXMsg->fWx;
return true;
}
plIMouseYEventMsg* pYMsg = plIMouseYEventMsg::ConvertNoRef(msg);
if (pYMsg)
{
// send a mouse event
plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg;
pMsg->SetXPos( fXPos );
if (pYMsg->fY == 999)
pMsg->SetYPos( fYPos + 0.01f );
else
if (pYMsg->fY == -999)
pMsg->SetYPos( fYPos - 0.01f );
else
pMsg->SetYPos(pYMsg->fY);
pMsg->SetDX(0);
pMsg->SetDY(fYPos - pMsg->GetYPos());
if (pMsg->GetDY() == 0.0f && !plMouseDevice::bMsgAlways)
{
delete pMsg;
return true;
}
if (fYMsg)
delete fYMsg;
fYMsg = pMsg;
if (pYMsg->fY == 999)
fYPos += 0.01;
else
if (pYMsg->fY == -999)
fYPos -= 0.01;
else
fYPos = pYMsg->fY;
fWYPos = pYMsg->fWy;
SetCursorY(fYPos);
return true;
}
plIMouseBEventMsg* pBMsg = plIMouseBEventMsg::ConvertNoRef(msg);
if (pBMsg)
{
// send a mouse event
plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg;
pMsg->SetXPos( fXPos );
pMsg->SetYPos( fYPos );
pMsg->SetDX(0);
pMsg->SetDY(0);
bool deleteMe = true;
// which button is different?
if (pBMsg->fButton & kLeftButtonDown && !(fButtonState & kLeftButtonDown))
{
// left button now down
fButtonState |= kLeftButtonDown;
pMsg->SetButton( kLeftButtonDown );
deleteMe = false;
}
else
if (pBMsg->fButton & kLeftButtonUp && fButtonState & kLeftButtonDown)
{
// left button now up
fButtonState &= ~kLeftButtonDown;
pMsg->SetButton( kLeftButtonUp );
deleteMe = false;
}
else
if (pBMsg->fButton & kRightButtonDown && !(fButtonState & kRightButtonDown))
{
// right button now down
fButtonState |= kRightButtonDown;
pMsg->SetButton( kRightButtonDown );
deleteMe = false;
}
else
if (pBMsg->fButton & kRightButtonUp && fButtonState & kRightButtonDown)
{
// right button now up
fButtonState &= ~kRightButtonDown;
pMsg->SetButton( kRightButtonUp );
deleteMe = false;
}
else
if (pBMsg->fButton & kMiddleButtonDown && !(fButtonState & kMiddleButtonDown))
{
// mouse wheel button now down
fButtonState |= kMiddleButtonDown;
pMsg->SetButton( kMiddleButtonDown );
deleteMe = false;
}
else
if (pBMsg->fButton & kMiddleButtonUp && fButtonState & kMiddleButtonDown)
{
// right button now up
fButtonState &= ~kMiddleButtonDown;
pMsg->SetButton( kMiddleButtonUp );
deleteMe = false;
}
if (pBMsg->fButton & kRightButtonDblClk)
{
// right button dbl clicked, send TWO messages
plMouseEventMsg* pMsg2 = TRACKED_NEW plMouseEventMsg;
pMsg2->SetXPos( fXPos );
pMsg2->SetYPos( fYPos );
pMsg2->SetDX(0);
pMsg2->SetDY(0);
pMsg2->SetButton( kRightButtonDblClk );
if( fB2Msg != nil )
delete fB2Msg;
fB2Msg = pMsg2;
pMsg->SetButton( kRightButtonDown );
deleteMe = false;
}
else
if (pBMsg->fButton & kLeftButtonDblClk)
{
// left button dbl clicked, send TWO messages
plMouseEventMsg* pMsg2 = TRACKED_NEW plMouseEventMsg;
pMsg2->SetXPos( fXPos );
pMsg2->SetYPos( fYPos );
pMsg2->SetDX(0);
pMsg2->SetDY(0);
pMsg2->SetButton( kLeftButtonDblClk );
if( fB2Msg != nil )
delete fB2Msg;
fB2Msg = pMsg2;
pMsg->SetButton( kLeftButtonDown );
deleteMe = false;
}
if( deleteMe )
{
// mouse button state not changed
delete pMsg;
return true;
}
// we are going to save up to two button mouse events per button (left and right)
// that will be dispatched on the next eval
// which button is this for?
if ( pMsg->GetButton() == kLeftButtonDown || pMsg->GetButton() == kLeftButtonUp )
{
// see if the queue is just empty
if ( fLeftBMsg[0] == nil)
{
// nothing to think about... goes in first slot
fLeftBMsg[0] = pMsg;
}
else if (fLeftBMsg[1] == nil)
{
// nothing to think about... goes in second slot
fLeftBMsg[1] = pMsg;
}
else
{
// else queue if full... need to make some decisions
plMouseEventMsg* lastMsg = plMouseEventMsg::ConvertNoRef(pMsg);
// ...if this is an up event and [1] is a down then we need to remove both
// ...because we can't lose the up event and the down will have no match
if ( pMsg->GetButton() == kLeftButtonUp && lastMsg && lastMsg->GetButton() == kLeftButtonDown)
{
delete pMsg;
delete fLeftBMsg[1];
fLeftBMsg[1] = nil;
}
// ... otherwise ignore this event
else
{
delete pMsg;
}
}
}
else if ( pMsg->GetButton() == kRightButtonDown || pMsg->GetButton() == kRightButtonUp )
{
// see if the queue is just empty
if ( fRightBMsg[0] == nil)
{
// nothing to think about... goes in first slot
fRightBMsg[0] = pMsg;
}
else if (fRightBMsg[1] == nil)
{
// nothing to think about... goes in second slot
fRightBMsg[1] = pMsg;
}
else
{
// else queue if full... need to make some decisions
plMouseEventMsg* lastMsg = plMouseEventMsg::ConvertNoRef(pMsg);
// ...if this is an up event and [1] is a down then we need to remove both
// ...because we can't lose the up event and the down will have no match
if ( pMsg->GetButton() == kRightButtonUp && lastMsg && lastMsg->GetButton() == kRightButtonDown)
{
delete pMsg;
delete fRightBMsg[1];
fRightBMsg[1] = nil;
}
// ... otherwise ignore this event
else
{
delete pMsg;
}
}
}
else if ( pMsg->GetButton() == kMiddleButtonDown || pMsg->GetButton() == kMiddleButtonUp )
{
// see if the queue is just empty
if ( fMiddleBMsg[0] == nil)
{
// nothing to think about... goes in first slot
fMiddleBMsg[0] = pMsg;
}
else if (fMiddleBMsg[1] == nil)
{
// nothing to think about... goes in second slot
fMiddleBMsg[1] = pMsg;
}
else
{
// else queue if full... need to make some decisions
plMouseEventMsg* lastMsg = plMouseEventMsg::ConvertNoRef(pMsg);
// ...if this is an up event and [1] is a down then we need to remove both
// ...because we can't lose the up event and the down will have no match
if ( pMsg->GetButton() == kMiddleButtonUp && lastMsg && lastMsg->GetButton() == kMiddleButtonDown)
{
delete pMsg;
delete fMiddleBMsg[1];
fMiddleBMsg[1] = nil;
}
// ... otherwise ignore this event
else
{
delete pMsg;
}
}
}
// we are going to dispatch the mouse button events right away
// and not wait for the next eval, because we shouldn't miss one of these
return true;
}
return false;
}
void plMouseDevice::HandleWindowActivate(bool bActive, HWND hWnd)
{
if ( bActive )
{
RECT rect;
::GetClientRect(hWnd,&rect);
// rect.right /= plInputManager::GetInstance()->GetMouseScale();
// rect.bottom /= plInputManager::GetInstance()->GetMouseScale();
::MapWindowPoints( hWnd, NULL, (POINT *)&rect, 2 );
::ClipCursor(&rect);
::ShowCursor( FALSE );
SetCapture(hWnd);
}
else
{
ReleaseCapture();
::ClipCursor(nil);
::ShowCursor( TRUE );
}
}

View File

@ -0,0 +1,227 @@
/*==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/>.
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==*/
// plInputDevice.h
#ifndef PL_INPUT_DEVICE_H
#define PL_INPUT_DEVICE_H
#include "HeadSpin.h"
#include "hsWindows.h"
//#include "../pnInputCore/plControlDefinition.h"
#include "../pnInputCore/plOSMsg.h"
#include "hsBitVector.h"
#include "hsTemplates.h"
#include "../../apps/plClient/res/resource.h"
class plMessage;
enum plKeyDef;
struct plMouseInfo;
class plPipeline;
class plInputDevice
{
public:
enum Flags
{
kDisabled = 0x1
};
protected:
UInt32 fFlags;
public:
plInputDevice() {;}
virtual ~plInputDevice() {;}
virtual const char* GetInputName() = 0;
UInt32 GetFlags() { return fFlags; }
void SetFlags(UInt32 f) { fFlags = f; }
virtual void HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat) {;}
virtual void HandleMouseEvent(plOSMsg message, plMouseState state) {;}
virtual void HandleWindowActivate(bool bActive, HWND hWnd) {;}
virtual hsBool MsgReceive(plMessage* msg) {return false;}
virtual void Shutdown() {;}
};
class plKeyEventMsg;
class plKeyboardDevice : public plInputDevice
{
hsBool fAltKeyDown;
hsBool fShiftKeyDown;
hsBool fCtrlKeyDown;
hsBool fCapsLockKeyDown;
int fControlMode;
hsBool fCapsLockLock;
static bool fKeyboardState[256]; // virtual key code is the index, bool is whether it is down or not
static hsBool fIgnoreCapsLock; // set if we want it to ignore this key when translating characters (i.e. for chatting)
static hsBool fKeyIsDeadKey; // the key we just got was a dead key, store the value if you're a text input object
static plKeyboardDevice* fInstance;
void InitKeyboardMaps();
void InitKeyboardState();
void ReleaseAllKeys();
public:
enum
{
CONSOLE_MODE = 0,
CONSOLE_FULL,
STANDARD_MODE,
};
plKeyboardDevice();
~plKeyboardDevice();
void SetControlMode(int i) { fControlMode = i; }
const char* GetInputName() { return "keyboard"; }
void HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat);
virtual void HandleWindowActivate(bool bActive, HWND hWnd);
virtual hsBool IsCapsLockKeyOn();
virtual void Shutdown();
#if HS_BUILD_FOR_WIN32
void ForceNumLock(hsBool on);
#endif
static hsBool IgnoreCapsLock() { return fIgnoreCapsLock; }
static void IgnoreCapsLock(hsBool ignore) { fIgnoreCapsLock = ignore; }
static hsBool KeyIsDeadKey() { return fKeyIsDeadKey; }
static plKeyboardDevice* GetInstance() { return fInstance; }
static char KeyEventToChar( plKeyEventMsg *msg );
protected:
hsBool fStartedUpWithNumLockOn; // maintaining a separate flag since apparently the other one can get confused
hsBool fPrevNumLockOn;
};
class plPlate;
#define CURSOR_UP IDB_CURSOR_UP
#define CURSOR_DOWN IDB_CURSOR_DOWN
#define CURSOR_RIGHT IDB_CURSOR_RIGHT
#define CURSOR_LEFT IDB_CURSOR_LEFT
#define CURSOR_OPEN IDB_CURSOR_OPEN
#define CURSOR_GRAB IDB_CURSOR_GRAB
#define CURSOR_CLICKED IDB_CURSOR_CLICKED
#define CURSOR_POISED IDB_CURSOR_POISED
#define CURSOR_ARROW IDB_CURSOR_ARROW
#define CURSOR_4WAY_OPEN IDB_CURSOR_4WAYOPEN
#define CURSOR_4WAY_CLOSED IDB_CURSOR_4WAYCLOSED
#define CURSOR_UPDOWN_CLOSED IDB_CURSOR_UPDOWNCLOSED
#define CURSOR_UPDOWN_OPEN IDB_CURSOR_UPDOWNOPEN
#define CURSOR_LEFTRIGHT_CLOSED IDB_CURSOR_LEFTRIGHTCLOSED
#define CURSOR_LEFTRIGHT_OPEN IDB_CURSOR_LEFTRIGHTOPEN
#define CURSOR_OFFER_BOOK IDB_CURSOR_BOOK
#define CURSOR_OFFER_BOOK_HI IDB_CURSOR_BOOK_HIGHLIGHT
#define CURSOR_OFFER_BOOK_CLICKED IDB_CURSOR_BOOK_CLICKED
#define CURSOR_CLICK_DISABLED IDB_CURSOR_DISABLED
#define CURSOR_HAND IDB_CURSOR_HAND
#define CURSOR_UPWARD IDB_CURSOR_UPWARD
class plInputEventMsg;
class plMouseDevice : public plInputDevice
{
public:
plMouseDevice();
~plMouseDevice();
const char* GetInputName() { return "mouse"; }
void HandleWindowActivate(bool bActive, HWND hWnd);
hsBool HasControlFlag(int f) const { return fControlFlags.IsBitSet(f); }
void SetControlFlag(int f)
{
fControlFlags.SetBit(f);
}
void ClearControlFlag(int which) { fControlFlags.ClearBit( which ); }
void SetCursorX(hsScalar x);
void SetCursorY(hsScalar y);
hsScalar GetCursorX() { return fXPos; }
hsScalar GetCursorY() { return fYPos; }
UInt32 GetButtonState() { return fButtonState; }
hsScalar GetCursorOpacity() { return fOpacity; }
void SetDisplayResolution(hsScalar Width, hsScalar Height);
virtual hsBool MsgReceive(plMessage* msg);
static plMouseDevice* Instance() { return plMouseDevice::fInstance; }
static void SetMsgAlways(bool b) { plMouseDevice::bMsgAlways = b; }
static void ShowCursor(hsBool override = false);
static void NewCursor(int cursor);
static void HideCursor(hsBool override = false);
static bool GetHideCursor() { return plMouseDevice::bCursorHidden; }
static void SetCursorOpacity( hsScalar opacity = 1.f );
static bool GetInverted() { return plMouseDevice::bInverted; }
static void SetInverted(bool inverted) { plMouseDevice::bInverted = inverted; }
static void AddNameToCursor(const char* name);
static void AddIDNumToCursor(UInt32 idNum);
static void AddCCRToCursor();
protected:
plInputEventMsg* fXMsg;
plInputEventMsg* fYMsg;
plInputEventMsg* fB2Msg;
// mouse button event queues (only hold 2)
plInputEventMsg* fLeftBMsg[2];
plInputEventMsg* fRightBMsg[2];
plInputEventMsg* fMiddleBMsg[2];
hsScalar fXPos;
hsScalar fYPos;
int fWXPos; // the windows coordinates of the cursor
int fWYPos;
UInt32 fButtonState;
hsScalar fOpacity;
hsBitVector fControlFlags;
plPlate *fCursor;
int fCursorID;
static plMouseDevice* fInstance;
static plMouseInfo fDefaultMouseControlMap[];
void CreateCursor( int cursor );
void IUpdateCursorSize();
static bool bMsgAlways;
static bool bCursorHidden;
static bool bCursorOverride;
static bool bInverted;
static hsScalar fWidth, fHeight;
};
#endif // PL_INPUT_DEVICE_H

View File

@ -0,0 +1,289 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plInputInterface.cpp - A single layer on the input interface stack //
// //
//// History /////////////////////////////////////////////////////////////////
// //
// 2.20.02 mcn - Created. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsConfig.h"
#include "hsWindows.h"
#include "hsTypes.h"
#include "plInputInterface.h"
#include "plInputInterfaceMgr.h"
#include "../pnInputCore/plKeyMap.h"
#include "../plMessage/plInputEventMsg.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
//// Constructor/Destructor //////////////////////////////////////////////////
plInputInterface::plInputInterface()
{
fEnabled = false;
fControlMap = TRACKED_NEW plKeyMap;
}
plInputInterface::~plInputInterface()
{
delete fControlMap;
}
void plInputInterface::ClearKeyMap()
{
if( fControlMap != nil )
fControlMap->ClearAll();
}
//// Read/Write //////////////////////////////////////////////////////////////
void plInputInterface::Read( hsStream* s, hsResMgr* mgr )
{
}
void plInputInterface::Write( hsStream* s, hsResMgr* mgr )
{
}
//// Helper Functions ////////////////////////////////////////////////////////
hsBool plInputInterface::IOwnsControlCode( ControlEventCode code )
{
if( fControlMap->FindBinding( code ) != nil )
return true;
return false;
}
//// IVerifyShiftKey /////////////////////////////////////////////////////////
// special logic so the shift key can make everyone totally happy...
hsBool plInputInterface::IVerifyShiftKey( plKeyDef key, int index )
{
// if we are mapped to the actual shift key, return true
if (key == KEY_SHIFT)
return true;
// if anything else is mapped to this key + shift, return false
/* for (int i=0; i < fControlMap->GetNumBindings(); i++)
{
if (index == i)
continue;
if (fKeyMap->fMap[i]->fKeyDef == key && fKeyMap->fMap[i]->fKeyFlags & plKeyInfo::kKeyShift )
return false;
}
*/ return true;
}
void plInputInterface::IDeactivateBinding(const plKeyBinding *binding)
{
if( !(binding->GetCodeFlags() & kControlFlagNoDeactivate) && !(binding->GetCodeFlags() & kControlFlagToggle) )
{
plCtrlCmd *pCmd = TRACKED_NEW plCtrlCmd( this );
pCmd->fControlCode = binding->GetCode();
pCmd->fControlActivated = false;
pCmd->SetCmdString( binding->GetExtendedString() );
pCmd->fNetPropagateToPlayers = ( binding->GetCodeFlags() & kControlFlagNetPropagate ) ? true : false;
fMessageQueue->Append( pCmd );
}
IClearKeyControlFlag(binding->GetCode());
}
//// ProcessKeyBindings //////////////////////////////////////////////////////
// Processes the given key event as a key binding, if one exists. If not,
// returns false.
hsBool plInputInterface::ProcessKeyBindings( plInputEventMsg *msg )
{
int i;
hsBool activate;
plKeyEventMsg *keyMsg = plKeyEventMsg::ConvertNoRef( msg );
if( keyMsg == nil )
return false;
/// We might have controls that are currently enabled that are triggered in part by
/// modifiers (ctrl or shift)...if that is true, then we want to disable them if either
/// of those modifiers are up, no matter what key this message is for
hsTArray<Int16> enabledCtrls;
fKeyControlFlags.Enumerate( enabledCtrls );
for( i = 0; i < enabledCtrls.GetCount(); i++ )
{
const plKeyBinding *binding = fControlMap->FindBinding( (ControlEventCode)enabledCtrls[ i ] );
if( binding == nil )
; // Somehow we lost the binding??
else
{
bool wantShift, wantCtrl;
if( fKeyControlsFrom2ndKeyFlags.IsBitSet( enabledCtrls[ i ] ) )
{
wantShift = ( binding->GetKey2().fFlags & plKeyCombo::kShift ) || ( binding->GetKey2().fKey == KEY_SHIFT );
wantCtrl = ( binding->GetKey2().fFlags & plKeyCombo::kCtrl ) || ( binding->GetKey2().fKey == KEY_CTRL );
}
else
{
wantShift = ( binding->GetKey1().fFlags & plKeyCombo::kShift ) || ( binding->GetKey1().fKey == KEY_SHIFT );
wantCtrl = ( binding->GetKey1().fFlags & plKeyCombo::kCtrl ) || ( binding->GetKey1().fKey == KEY_CTRL );
}
if( ( wantShift && !keyMsg->GetShiftKeyDown() ) || ( wantCtrl && !keyMsg->GetCtrlKeyDown() ) )
{
IDeactivateBinding(binding);
fKeyControlsFrom2ndKeyFlags.SetBit(enabledCtrls[i], false);
}
}
}
/// Process any binding for this message's key code now
plKeyCombo combo( keyMsg->GetKeyCode(), ( keyMsg->GetShiftKeyDown() ? plKeyCombo::kShift : 0 ) |
( keyMsg->GetCtrlKeyDown() ? plKeyCombo::kCtrl : 0 ) );
hsTArray<const plKeyBinding *> bindings;
fControlMap->FindAllBindingsByKey(combo, bindings);
// The first binding is the one we want. (FindAllBindingsByKey guarantees this)
const plKeyBinding *binding = (bindings.GetCount() ? bindings[0] : nil);
// If other bindings were found, they lose out to the first one.
for (i = 1; i < bindings.GetCount(); i++)
IDeactivateBinding(bindings[i]);
/*
const plKeyBinding *binding = fControlMap->FindBindingByKey( combo );
if( binding == nil )
{
// Don't panic just yet, there are some special cases with the shift key to check first
if( keyMsg->GetKeyCode() == KEY_SHIFT || keyMsg->GetShiftKeyDown() )
{
// See, there are two other cases to consider: 1) we have a binding directly to the shift
// key, which wouldn't have the shift flag set (so the above search wouldn't have caught it).
// The second case would be if we have a matching binding without shift...
// which is VALID so long as no other bindings respond to this key combo + shift, but of course,
// if there were, we'd have found them already!
// Either way, we remove the shift flag and try again
combo.fFlags &= ~plKeyCombo::kShift;
binding = fControlMap->FindBindingByKey( combo );
}
}
*/
if (!binding)
return false;
UInt32 codeFlags = binding->GetCodeFlags();
// Filter out no-repeat messages
if( ( codeFlags & kControlFlagNoRepeat ) && keyMsg->GetRepeat() )
return false;
if( codeFlags & kControlFlagNormal )
{
// "Normal" behavior--enable on key down, disable on key up
activate = keyMsg->GetKeyDown() ? true : false;
}
else if( codeFlags & kControlFlagToggle )
{
// Toggle behavior
if( ( codeFlags & kControlFlagDownEvent ) && !keyMsg->GetKeyDown() )
return false;
if( ( codeFlags & kControlFlagUpEvent ) && keyMsg->GetKeyDown() )
return false;
if( IHasKeyControlFlag( binding->GetCode() ) )
activate = false;
else
activate = true;
}
else
{
// Remaining ones are triggered to activate on their flagged event and
// deactivate when that turns false
if( ( codeFlags & kControlFlagDownEvent ) && !keyMsg->GetKeyDown() )
activate = false;
else if( ( codeFlags & kControlFlagUpEvent ) && keyMsg->GetKeyDown() )
activate = false;
else
activate = true;
}
hsBool wasActive = IHasKeyControlFlag(binding->GetCode());
// Set or clear our flags, since we do that even if we don't send a message
if( activate )
{
ISetKeyControlFlag( binding->GetCode() );
fKeyControlsFrom2ndKeyFlags.SetBit( binding->GetCode(), ( binding->GetKey2() == combo ) ? true : false );
}
else
{
IClearKeyControlFlag( binding->GetCode() );
fKeyControlsFrom2ndKeyFlags.SetBit( binding->GetCode(), 0 );
}
// Filter out codes that only want their activate messages sent (like console commands)
if( ( codeFlags & kControlFlagNoDeactivate ) && !activate )
return false;
if (!IControlCodeEnabled(binding->GetCode()))
{
if (activate || (codeFlags & kControlFlagToggle) || !wasActive)
{
// It's ok to deactivate a disabled control, but not activate it
return false;
}
}
/// OK, generate the message to send
plCtrlCmd *pCmd = TRACKED_NEW plCtrlCmd( this );
pCmd->fControlCode = binding->GetCode();
pCmd->fControlActivated = activate;
pCmd->SetCmdString( binding->GetExtendedString() );
pCmd->fNetPropagateToPlayers = ( codeFlags & kControlFlagNetPropagate ) ? true : false;
fMessageQueue->Append( pCmd );
return true;
}
hsBool plInputInterface::IControlCodeEnabled(ControlEventCode code )
{
return (!fDisabledControls.IsBitSet(code));
}

View File

@ -0,0 +1,206 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plInputInterface.cpp - A single layer on the input interface stack //
// //
//// History /////////////////////////////////////////////////////////////////
// //
// 2.20.02 mcn - Created. //
// //
//// Note on GetPriorityLevel() //////////////////////////////////////////////
// //
// The inputInterfaceMgr uses GetPriorityLevel() to place each interface //
// into the stack relative to the other interfaces. Current priority //
// levels are: //
// Console - 100 //
// GUI system - 75 //
// Avatar input - 50 //
// Scene interaction - 25 //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plInputInterface_h
#define _plInputInterface_h
#include "hsRefCnt.h"
#include "hsTemplates.h"
#include "hsBitVector.h"
// Needed for UNIX Build
// only windows will let you predeclare an enum
#include "../../NucleusLib/pnInputCore/plKeyDef.h"
#include "../../NucleusLib/pnInputCore/plControlEventCodes.h"
//// Class Definition ////////////////////////////////////////////////////////
class hsStream;
class hsResMgr;
class plInputEventMsg;
class plInputInterfaceMgr;
class plMessage;
class plKeyMap;
class plCtrlCmd;
class plKeyBinding;
class plInputInterface : public hsRefCnt
{
friend class plInputInterfaceMgr;
protected:
enum Priorities
{
kConsolePriority = 100,
kGUISystemPriority = 75,
kDebugCmdPrioity = 60,
kSceneInteractionPriority = 50,
kTelescopeInputPriority = 26,
kAvatarInputPriority = 25,
};
plInputInterfaceMgr *fManager;
plKeyMap *fControlMap;
hsTArray<plCtrlCmd *> *fMessageQueue;
hsBitVector fKeyControlFlags;
hsBitVector fKeyControlsFrom2ndKeyFlags;
hsBitVector fDisabledControls;
hsBool fEnabled;
void ISetMessageQueue( hsTArray<plCtrlCmd *> *queue ) { fMessageQueue = queue; }
plKeyMap *IGetControlMap( void ) const { return fControlMap; }
hsBool IOwnsControlCode( ControlEventCode code );
hsBool IVerifyShiftKey( plKeyDef key, int index );
hsBool IHasKeyControlFlag(int f) const { return fKeyControlFlags.IsBitSet(f); }
void ISetKeyControlFlag(int f) { fKeyControlFlags.SetBit(f); }
void IClearKeyControlFlag(int which) { fKeyControlFlags.ClearBit( which ); }
void IDisableControl(int which) { fDisabledControls.SetBit(which); }
void IEnableControl(int which) { fDisabledControls.ClearBit(which); }
// The binding lost focus/priority. Behave as though they released the key and send
// a deactivate message for the control code.
void IDeactivateBinding(const plKeyBinding *binding);
// Gets called once per IUpdate(), just like normal IEval()s
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ) { return false; }
// Override to handle special-cased control messages of your own (same as receiving them via a message, but if you process them, nobody else gets them). Return false if you don't handle it.
virtual hsBool IHandleCtrlCmd( plCtrlCmd *cmd ) { return false; }
// Override to let the input interfaces control when a binding is truly active. If this function returns false,
// ProcessKeyBindings will ignore the keypress for the given control. This way, the interfaces can be selective
// about which bindings are active when. By default, always returns true, since there are very few and rare
// cases where you'd want to return false
virtual hsBool IControlCodeEnabled( ControlEventCode code );
// Some helpers for derived classes to avoid including the manager unnecessariliy
public:
plInputInterface();
virtual ~plInputInterface();
enum Cursors
{
kNullCursor = 0,
kCursorUp,
kCursorLeft,
kCursorRight,
kCursorDown,
kCursorPoised,
kCursorClicked,
kCursorUnClicked,
kCursorHidden,
kCursorOpen,
kCursorGrab,
kCursorArrow,
kCursor4WayDraggable,
kCursor4WayDragging,
kCursorUpDownDraggable,
kCursorUpDownDragging,
kCursorLeftRightDraggable,
kCursorLeftRightDragging,
kCursorOfferBook,
kCursorOfferBookHilite,
kCursorOfferBookClicked,
kCursorClickDisabled,
kCursorHand,
kCursorUpward,
};
virtual void Read( hsStream* s, hsResMgr* mgr );
virtual void Write( hsStream* s, hsResMgr* mgr );
// Returns the priority of this interface layer, based on the Priorities enum
virtual UInt32 GetPriorityLevel( void ) const = 0;
// Returns true if the message was handled, false if not and we want to pass it on to others in the stack
virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ) = 0;
// Returns the currently active mouse cursor for this layer, as defined in pnMessage/plCursorChangeMsg.h
virtual UInt32 GetCurrentCursorID( void ) const = 0;
// Returns the current opacity that this layer wants the cursor to be, from 0 (xparent) to 1 (opaque)
virtual hsScalar GetCurrentCursorOpacity( void ) const { return 1.f; }
// Returns true if this layer is wanting to change the mouse, false if it isn't interested
virtual hsBool HasInterestingCursorID( void ) const = 0;
// Gets called by the manager. If you want a message to come to you, set your manager as the destination
virtual hsBool MsgReceive( plMessage *msg ) { return false; }
// Any initialization that requires a pointer to the manager needs to be done on Init()/Shutdown()
virtual void Init( plInputInterfaceMgr *manager ) { fManager = manager; RestoreDefaultKeyMappings(); }
virtual void Shutdown( void ) {}
// Gets called when any of the key mappings are changed, so that the interface layer can refresh the ones its interested in
virtual void RefreshKeyMap( void ) {}
// Called when the interface manager is setting all key mappings to default
virtual void RestoreDefaultKeyMappings( void ) {}
// Called on each interface layer that gets missed when processing inputEvents in the manager (i.e. you either get this call or InterpretInputEvent)
virtual void MissedInputEvent( plInputEventMsg *pMsg ) {}
// Non-virtual, can't override--processes an inputEventMsg to see if we can handle it via a key binding (if so, InterpretInputEvent won't be called)
hsBool ProcessKeyBindings( plInputEventMsg *keyMsg );
void SetEnabled( hsBool e ) { fEnabled = e; }
hsBool IsEnabled( void ) const { return fEnabled; }
// clear all keys from map
virtual void ClearKeyMap();
// reset clickable state
virtual void ResetClickableState() {;}
};
#endif // _plInputInterface_h

View File

@ -0,0 +1,947 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plInputInterfaceMgr.cpp - The manager of all input interface layers //
// //
//// History /////////////////////////////////////////////////////////////////
// //
// 2.20.02 mcn - Created. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsConfig.h"
#include "hsWindows.h"
#include "hsTypes.h"
#include "plInputInterfaceMgr.h"
#include "plInputInterface.h"
#include "plInputDevice.h" // For mouse device stuff
#include "../pnInputCore/plKeyMap.h"
#include "../plMessage/plInputEventMsg.h"
#include "../plMessage/plInputIfaceMgrMsg.h"
#include "../pnMessage/plClientMsg.h"
#include "../pnMessage/plTimeMsg.h"
#include "../pnMessage/plCmdIfaceModMsg.h"
#include "../pnMessage/plPlayerPageMsg.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnKeyedObject/plFixedKey.h"
#include "../pnNetCommon/plNetApp.h"
#include "../plNetClient/plNetClientMgr.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
#include "plProfile.h"
#include "../plResMgr/plLocalization.h"
plProfile_CreateTimer("Input", "Update", Input);
//// plCtrlCmd ///////////////////////////////////////////////////////////////
void plCtrlCmd::Write(hsStream* stream, hsResMgr* mgr)
{
stream->WriteSwap32( fControlCode );
stream->WriteBool( fControlActivated );
fPt.Write(stream);
// write cmd/string
plMsgCStringHelper::Poke(fCmd, stream);
}
void plCtrlCmd::Read(hsStream* stream, hsResMgr* mgr)
{
fControlCode = (ControlEventCode)stream->ReadSwap32();
fControlActivated = stream->ReadBool();
fPt.Read(stream);
// read cmd/string
plMsgCStringHelper::Peek(fCmd, stream);
}
//// plDefaultKeyCatcher /////////////////////////////////////////////////////
plDefaultKeyCatcher::~plDefaultKeyCatcher()
{
if( plInputInterfaceMgr::GetInstance() != nil )
plInputInterfaceMgr::GetInstance()->SetDefaultKeyCatcher( nil );
}
//// Statics /////////////////////////////////////////////////////////////////
plInputInterfaceMgr *plInputInterfaceMgr::fInstance = nil;
//// Constructor/Destructor //////////////////////////////////////////////////
plInputInterfaceMgr::plInputInterfaceMgr()
{
fClickEnabled = false;
fCurrentCursor = -1;
fCursorOpacity = 1.f;
fForceCursorHidden = false;
fForceCursorHiddenCount = 0;
InitDefaultKeyMap();
#if 0
// make sure we don't miss control keys on remote players
SetSynchFlagsBit(plSynchedObject::kSendReliably);
#endif
hsAssert( fInstance == nil, "Attempting to create two input interface managers!" );
fInstance = this;
fCurrentFocus = nil;
fDefaultCatcher = nil;
}
plInputInterfaceMgr::~plInputInterfaceMgr()
{
Shutdown();
fInstance = nil;
}
//// Init ////////////////////////////////////////////////////////////////////
#include "plAvatarInputInterface.h"
#include "plSceneInputInterface.h"
#include "plDebugInputInterface.h"
#include "plTelescopeInputInterface.h"
void plInputInterfaceMgr::Init( void )
{
RegisterAs( kInputInterfaceMgr_KEY );
plgDispatch::Dispatch()->RegisterForType( plInputIfaceMgrMsg::Index(), GetKey() );
plgDispatch::Dispatch()->RegisterForType( plInputEventMsg::Index(), GetKey() );
plgDispatch::Dispatch()->RegisterForType( plEvalMsg::Index(), GetKey() );
plgDispatch::Dispatch()->RegisterForExactType( plPlayerPageMsg::Index(), GetKey() );
plgDispatch::Dispatch()->RegisterForExactType( plCmdIfaceModMsg::Index(), GetKey() );
plgDispatch::Dispatch()->RegisterForExactType( plClientMsg::Index(), GetKey() );
/// Hacks (?) for now
plAvatarInputInterface *avatar = TRACKED_NEW plAvatarInputInterface();
IAddInterface( avatar );
hsRefCnt_SafeUnRef( avatar );
plSceneInputInterface *scene = TRACKED_NEW plSceneInputInterface();
IAddInterface( scene );
hsRefCnt_SafeUnRef( scene );
plDebugInputInterface *camDrive = TRACKED_NEW plDebugInputInterface();
IAddInterface( camDrive );
hsRefCnt_SafeUnRef( camDrive );
}
//// Shutdown ////////////////////////////////////////////////////////////////
void plInputInterfaceMgr::Shutdown( void )
{
int i;
// WriteKeyMap();
for( i = 0; i < fInterfaces.GetCount(); i++ )
{
fInterfaces[ i ]->Shutdown();
hsRefCnt_SafeUnRef( fInterfaces[ i ] );
}
fInterfaces.Reset();
for( i = 0; i < fMessageQueue.GetCount(); i++ )
delete fMessageQueue[ i ];
fMessageQueue.Reset();
plgDispatch::Dispatch()->UnRegisterForType( plInputIfaceMgrMsg::Index(), GetKey() );
plgDispatch::Dispatch()->UnRegisterForType( plInputEventMsg::Index(), GetKey() );
plgDispatch::Dispatch()->UnRegisterForType( plEvalMsg::Index(), GetKey() );
plgDispatch::Dispatch()->UnRegisterForExactType( plPlayerPageMsg::Index(), GetKey() );
plgDispatch::Dispatch()->UnRegisterForExactType( plCmdIfaceModMsg::Index(), GetKey() );
plgDispatch::Dispatch()->UnRegisterForExactType( plClientMsg::Index(), GetKey() );
UnRegisterAs( kInputInterfaceMgr_KEY );
}
//// IAdd/RemoveInterface ////////////////////////////////////////////////////
// Each interface has a "priority level", i.e. where in the list it should be.
// Doing it this way allows us to keep the manager unaware of the number or
// types of interfaces.
void plInputInterfaceMgr::IAddInterface( plInputInterface *iface )
{
int i;
for( i = 0; i < fInterfaces.GetCount(); i++ )
{
if( fInterfaces[ i ]->GetPriorityLevel() < iface->GetPriorityLevel() )
break;
}
fInterfaces.Insert( i, iface );
hsRefCnt_SafeRef( iface );
iface->Init( this );
iface->ISetMessageQueue( &fMessageQueue );
}
void plInputInterfaceMgr::IRemoveInterface( plInputInterface *iface )
{
int idx = fInterfaces.Find( iface );
if( idx != fInterfaces.kMissingIndex )
{
fInterfaces[ idx ]->Shutdown();
hsRefCnt_SafeUnRef( fInterfaces[ idx ] );
fInterfaces.Remove( idx );
}
}
/// reset clickable state //////////////////////////////////////////////////////////////////////////
void plInputInterfaceMgr::ResetClickableState()
{
// look for the scene input interface
for(int i = 0; i < fInterfaces.GetCount(); i++ )
fInterfaces[i]->ResetClickableState();
}
//// IUpdateCursor ///////////////////////////////////////////////////////////
void plInputInterfaceMgr::IUpdateCursor( Int32 newCursor )
{
int mouseCursorResID;
fCurrentCursor = newCursor;
if( fCurrentCursor == plInputInterface::kCursorHidden )
plMouseDevice::HideCursor();
else
{
plMouseDevice::ShowCursor();
switch( fCurrentCursor )
{
case plInputInterface::kCursorUp: mouseCursorResID = CURSOR_UP; break;
case plInputInterface::kCursorLeft: mouseCursorResID = CURSOR_LEFT; break;
case plInputInterface::kCursorRight: mouseCursorResID = CURSOR_RIGHT; break;
case plInputInterface::kCursorDown: mouseCursorResID = CURSOR_DOWN; break;
case plInputInterface::kCursorPoised: mouseCursorResID = CURSOR_POISED; break;
case plInputInterface::kCursorClicked: mouseCursorResID = CURSOR_CLICKED; break;
case plInputInterface::kCursorUnClicked: mouseCursorResID = CURSOR_POISED; break;
case plInputInterface::kCursorOpen: mouseCursorResID = CURSOR_OPEN; break;
case plInputInterface::kCursorGrab: mouseCursorResID = CURSOR_GRAB; break;
case plInputInterface::kCursorArrow: mouseCursorResID = CURSOR_ARROW; break;
case plInputInterface::kCursor4WayDraggable: mouseCursorResID = CURSOR_4WAY_OPEN; break;
case plInputInterface::kCursor4WayDragging: mouseCursorResID = CURSOR_4WAY_CLOSED; break;
case plInputInterface::kCursorUpDownDraggable: mouseCursorResID = CURSOR_UPDOWN_OPEN; break;
case plInputInterface::kCursorUpDownDragging: mouseCursorResID = CURSOR_UPDOWN_CLOSED; break;
case plInputInterface::kCursorLeftRightDraggable: mouseCursorResID = CURSOR_LEFTRIGHT_OPEN; break;
case plInputInterface::kCursorLeftRightDragging: mouseCursorResID = CURSOR_LEFTRIGHT_CLOSED; break;
case plInputInterface::kCursorOfferBook: mouseCursorResID = CURSOR_OFFER_BOOK; break;
case plInputInterface::kCursorOfferBookHilite: mouseCursorResID = CURSOR_OFFER_BOOK_HI; break;
case plInputInterface::kCursorOfferBookClicked: mouseCursorResID = CURSOR_OFFER_BOOK_CLICKED; break;
case plInputInterface::kCursorClickDisabled: mouseCursorResID = CURSOR_CLICK_DISABLED; break;
case plInputInterface::kCursorHand: mouseCursorResID = CURSOR_HAND; break;
case plInputInterface::kCursorUpward: mouseCursorResID = CURSOR_UPWARD; break;
default: mouseCursorResID = CURSOR_OPEN; break;
}
plMouseDevice::NewCursor( mouseCursorResID );
}
}
//// IEval ///////////////////////////////////////////////////////////////////
// Inherited from plSingleModifier, gets called once per IUpdate() loop.
hsBool plInputInterfaceMgr::IEval( double secs, hsScalar del, UInt32 dirty )
{
const char *inputEval = "Eval";
plProfile_BeginLap(Input, inputEval);
int i;
// Let all our layers eval
for( i = 0; i < fInterfaces.GetCount(); i++ )
fInterfaces[ i ]->IEval( secs, del, dirty );
// Handle our message queue now
for( i = 0; i < fMessageQueue.Count(); i++ )
{
// Can its layer handle it?
if( !fMessageQueue[ i ]->GetSource()->IHandleCtrlCmd( fMessageQueue[ i ] ) )
{
// Nope, just dispatch it like normal
plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg;
for (int j = 0; j < fReceivers.Count(); j++)
pMsg->AddReceiver( fReceivers[ j ] );
pMsg->SetControlActivated( fMessageQueue[i]->fControlActivated );
pMsg->SetControlCode( fMessageQueue[i]->fControlCode );
pMsg->SetControlPct(fMessageQueue[i]->fPct);
pMsg->SetTurnToPt( fMessageQueue[i]->fPt );
pMsg->SetCmdString(fMessageQueue[i]->GetCmdString());
pMsg->SetSender( GetKey() );
plgDispatch::MsgSend( pMsg );
///////////////////////////////////////////////////////
// send same msg over network to players
///////////////////////////////////////////////////////
if (fMessageQueue[i]->fNetPropagateToPlayers)
{
pMsg = TRACKED_NEW plControlEventMsg;
for (int j = 0; j < fReceivers.Count(); j++)
if (fReceivers[j] == plNetClientApp::GetInstance()->GetLocalPlayerKey())
pMsg->AddReceiver( fReceivers[j] );
if (pMsg->GetNumReceivers())
{
pMsg->SetControlActivated( fMessageQueue[i]->fControlActivated );
pMsg->SetControlCode( fMessageQueue[i]->fControlCode );
pMsg->SetControlPct(fMessageQueue[i]->fPct);
pMsg->SetTurnToPt( fMessageQueue[i]->fPt );
pMsg->SetCmdString(fMessageQueue[i]->GetCmdString());
pMsg->SetSender( GetKey() );
pMsg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kPropagateToModifiers |
plMessage::kNetUseRelevanceRegions); // bcast only to other players who care about the region I'm in
pMsg->SetBCastFlag(plMessage::kLocalPropagate, false);
plgDispatch::MsgSend( pMsg );
}
else
delete pMsg;
}
}
}
// Clear the message queue
for( i = 0; i < fMessageQueue.Count(); i++ )
delete fMessageQueue[ i ];
fMessageQueue.SetCount( 0 );
plProfile_EndLap(Input, inputEval);
return true;
}
void plInputInterfaceMgr::ForceCursorHidden( hsBool requestedState )
{
if ( requestedState )
{
fForceCursorHiddenCount++;
fForceCursorHidden = requestedState;
}
else
{
fForceCursorHiddenCount--;
// this happens way too often to leave in
// hsAssert(fForceCursorHiddenCount>=0,"ForceCursorHidded: unhiding more times than hidden" );
#define OnlyHideCursorOnLast
#ifdef OnlyHideCursorOnLast
// is this is the last person... then really unforce hidding the mouse cursor
if ( fForceCursorHiddenCount <= 0 )
{
#endif //OnlyHideCursorOnLast
fForceCursorHidden = requestedState;
fForceCursorHiddenCount = 0;
#ifdef OnlyHideCursorOnLast
}
#endif //OnlyHideCursorOnLast
}
}
hsBool plInputInterfaceMgr::ICheckCursor(plInputInterface *iFace)
{
if( iFace->IsEnabled() && iFace->HasInterestingCursorID() )
{
if( iFace->GetCurrentCursorID() != fCurrentCursor )
IUpdateCursor( iFace->GetCurrentCursorID() );
if( iFace->GetCurrentCursorOpacity() != fCursorOpacity )
{
fCursorOpacity = iFace->GetCurrentCursorOpacity();
plMouseDevice::SetCursorOpacity( fCursorOpacity );
}
return true;
}
return false;
}
//// MsgReceive //////////////////////////////////////////////////////////////
hsBool plInputInterfaceMgr::MsgReceive( plMessage *msg )
{
int i;
plEvalMsg *pEvalMsg = plEvalMsg::ConvertNoRef( msg );
if( pEvalMsg )
{
IEval( pEvalMsg->GetTimeStamp(), pEvalMsg->DelSeconds(), false );
return true;
}
plInputEventMsg *ieMsg = plInputEventMsg::ConvertNoRef( msg );
if( ieMsg != nil )
{
const char *inputIEM = "InputEventMsg";
plProfile_BeginLap(Input, inputIEM);
hsBool handled = false;
UInt32 missedInputStartIdx = 0;
plInputInterface *oldCurrentFocus = fCurrentFocus;
// Current focus (if there is one) gets first crack
if( fCurrentFocus )
{
if( fCurrentFocus->IsEnabled() )
{
handled = (fCurrentFocus->ProcessKeyBindings(ieMsg) || fCurrentFocus->InterpretInputEvent(ieMsg));
}
}
if (!handled)
{
// Walk our stack
for( i = 0; i < fInterfaces.GetCount(); i++ )
{
if( fInterfaces[ i ]->IsEnabled() && fInterfaces[ i ] != oldCurrentFocus)
{
// Try the key bindings first (common for all layers)
if( fInterfaces[ i ]->ProcessKeyBindings( ieMsg ) || fInterfaces[ i ]->InterpretInputEvent( ieMsg ))
{
handled = true;
break;
}
}
}
if( !handled )
{
// Fell all the way through the stack...must've been a very uninteresting message...
if( plKeyEventMsg::ConvertNoRef( ieMsg ) && fDefaultCatcher != nil )
{
// But somebody loves those keys :)
fDefaultCatcher->HandleKeyEvent( plKeyEventMsg::ConvertNoRef( ieMsg ) );
}
}
missedInputStartIdx = i + 1;
}
// Notify the rest of the interfaces in the stack that they missed the event ("lost focus", as it were)
for (i = missedInputStartIdx; i < fInterfaces.GetCount(); i++)
if (fInterfaces[i] != oldCurrentFocus)
fInterfaces[i]->MissedInputEvent(ieMsg);
// Now we re-walk to see who's the new interested party. Note that we have to re-walk
// because a key down may have changed some layer's interest in the cursor
if( !fForceCursorHidden )
{
hsBool cursorHandled = false;
if (fCurrentFocus)
cursorHandled = ICheckCursor(fCurrentFocus);
if (!cursorHandled)
{
for( i = 0; i < fInterfaces.GetCount(); i++ )
{
if (ICheckCursor(fInterfaces[i]))
{
cursorHandled = true;
break;
}
}
if (!cursorHandled)
{
// NOBODY is interested in the mouse, so set to our default cursor
IUpdateCursor( plInputInterface::kCursorUp );
fCursorOpacity = 1.f;
plMouseDevice::SetCursorOpacity( fCursorOpacity );
}
}
}
else
{
// Special debug flag to force the cursor to be hidden
if( fCursorOpacity != 0.f )
{
fCursorOpacity = 0.f;
plMouseDevice::SetCursorOpacity( fCursorOpacity );
}
}
plProfile_EndLap(Input, inputIEM);
return true;
}
plInputIfaceMgrMsg *mgrMsg = plInputIfaceMgrMsg::ConvertNoRef( msg );
if( mgrMsg != nil )
{
if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kAddInterface )
{
IAddInterface( mgrMsg->GetIFace() );
return true;
}
else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kRemoveInterface )
{
IRemoveInterface( mgrMsg->GetIFace() );
return true;
}
else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kEnableClickables )
{
fClickEnabled = true;
}
else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kDisableClickables )
{
fClickEnabled = false;
}
}
plPlayerPageMsg *pPMsg = plPlayerPageMsg::ConvertNoRef( msg );
if( pPMsg != nil && !pPMsg->fUnload)
{
if( pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey() )
fReceivers.Append( pPMsg->fPlayer );
else
{
int idx = fReceivers.Find( pPMsg->fPlayer );
if( idx != fReceivers.kMissingIndex )
fReceivers.Remove( idx );
}
}
plCmdIfaceModMsg *pCMsg = plCmdIfaceModMsg::ConvertNoRef( msg );
if( pCMsg )
{
if( pCMsg->Cmd( plCmdIfaceModMsg::kAdd ) )
{
for( int i = 0; i < fReceivers.Count(); i++ )
{
if( fReceivers[i] == pCMsg->GetSender() )
return true;
}
fReceivers.Append( pCMsg->GetSender() );
return true;
}
else
if( pCMsg->Cmd( plCmdIfaceModMsg::kRemove ) )
{
for( int i = 0; i < fReceivers.Count(); i++ )
{
if( fReceivers[ i ] == pCMsg->GetSender() )
{
fReceivers.Remove( i );
break;
}
}
return true;
}
}
plClientMsg *cMsg = plClientMsg::ConvertNoRef(msg);
if (cMsg && cMsg->GetClientMsgFlag() == plClientMsg::kInitComplete)
{
// Backwards compatability hack:
// We've loaded in the user prefs for input. If they bind movement
// to an arrow, or numpad, and the other binding is free, automatically
// bind the other one.
plKeyMap *map = plAvatarInputInterface::GetInstance()->fControlMap;
map->HandleAutoDualBinding(KEY_UP, KEY_NUMPAD8);
map->HandleAutoDualBinding(KEY_DOWN, KEY_NUMPAD2);
map->HandleAutoDualBinding(KEY_LEFT, KEY_NUMPAD4);
map->HandleAutoDualBinding(KEY_RIGHT, KEY_NUMPAD6);
plgDispatch::Dispatch()->UnRegisterForExactType( plClientMsg::Index(), GetKey() );
return true;
}
// Wasn't one we want. Was it one that one of our interfaces wanted?
for( i = 0; i < fInterfaces.GetCount(); i++ )
{
if( fInterfaces[ i ]->MsgReceive( msg ) )
return true;
}
// Nothing, pass on...
return plSingleModifier::MsgReceive( msg );
}
//// Read/Write //////////////////////////////////////////////////////////////
void plInputInterfaceMgr::Read( hsStream* s, hsResMgr* mgr )
{
plSingleModifier::Read( s, mgr );
}
void plInputInterfaceMgr::Write( hsStream* s, hsResMgr* mgr )
{
plSingleModifier::Write( s, mgr );
}
//////////////////////////////////////////////////////////////////////////////
//// Key Maps ////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//// IGetRoutedKeyMap ////////////////////////////////////////////////////////
plKeyMap *plInputInterfaceMgr::IGetRoutedKeyMap( ControlEventCode code )
{
int i;
for( i = 0; i < fInterfaces.GetCount(); i++ )
{
if( fInterfaces[ i ]->IOwnsControlCode( code ) )
return fInterfaces[ i ]->fControlMap;
}
return nil;
}
//// IUnbind /////////////////////////////////////////////////////////////////
// Unmaps any mappings with the given key. This prevents you from mapping
// a single key to multiple commands. Currently inactive because some people
// think it's a good idea to be able to do that.
#define ALLOW_MULTIPLE_CMDS_PER_KEY 1
void plInputInterfaceMgr::IUnbind( const plKeyCombo &key )
{
#if !(ALLOW_MULTIPLE_CMDS_PER_KEY)
int i;
for( i = 0; i < fInterfaces.GetCount(); i++ )
fInterfaces[ i ]->fControlMap->UnmapKey( key );
#endif
}
void plInputInterfaceMgr::ClearAllKeyMaps()
{
for (int i = 0; i < fInterfaces.Count(); i++)
fInterfaces[i]->ClearKeyMap();
}
//// Binding Routers /////////////////////////////////////////////////////////
void plInputInterfaceMgr::BindAction( const plKeyCombo &key, ControlEventCode code )
{
plKeyMap *map = IGetRoutedKeyMap( code );
if( map != nil )
{
// Use default prefs
map->EnsureKeysClear( key, plKeyCombo::kUnmapped );
map->BindKey( key, code );
}
RefreshInterfaceKeyMaps();
}
void plInputInterfaceMgr::BindAction( const plKeyCombo &key1, const plKeyCombo &key2,
ControlEventCode code )
{
plKeyMap *map = IGetRoutedKeyMap( code );
if( map != nil )
{
// Force the bindings to each key, since the user specified both
map->EnsureKeysClear( key1, key2 );
map->BindKey( key1, code, plKeyMap::kFirstAlways );
map->BindKey( key2, code, plKeyMap::kSecondAlways );
}
RefreshInterfaceKeyMaps();
}
const plKeyBinding* plInputInterfaceMgr::FindBinding( ControlEventCode code )\
{
plKeyMap *map = IGetRoutedKeyMap( code );
if( map != nil )
{
// Use default prefs
return map->FindBinding(code);
}
return nil;
}
void plInputInterfaceMgr::BindConsoleCmd( const plKeyCombo &key, const char *cmd, plKeyMap::BindPref pref /*= kNoPreference*/ )
{
// not sure why this is not for external...since its done thru the different interfaces?
//#ifdef PLASMA_EXTERNAL_RELEASE
// return;
//#endif
plKeyMap *map = IGetRoutedKeyMap( B_CONTROL_CONSOLE_COMMAND );
if( map != nil )
{
// Default prefs again
map->EnsureKeysClear( key, plKeyCombo::kUnmapped );
// BindKeyToConsoleCmd only works if the console command in question has already been assigned
// to this map. Oftentimes, this isn't true when the user is binding console commands, so go ahead
// and add the command to this map. If it's already added, this call will just quietly fail and
// we're ok to continue
map->AddConsoleCommand( cmd );
map->BindKeyToConsoleCmd( key, cmd, pref );
}
RefreshInterfaceKeyMaps();
}
const plKeyBinding* plInputInterfaceMgr::FindBindingByConsoleCmd( const char *cmd )
{
plKeyMap *map = IGetRoutedKeyMap( B_CONTROL_CONSOLE_COMMAND );
if( map != nil )
{
return map->FindConsoleBinding(cmd);
}
return nil;
}
//// InitDefaultKeyMap ///////////////////////////////////////////////////////
void plInputInterfaceMgr::InitDefaultKeyMap( void )
{
int i;
for( i = 0; i < fInterfaces.GetCount(); i++ )
fInterfaces[ i ]->RestoreDefaultKeyMappings();
RefreshInterfaceKeyMaps();
}
//// RefreshInterfaceKeyMaps /////////////////////////////////////////////////
void plInputInterfaceMgr::RefreshInterfaceKeyMaps( void )
{
int i;
for( i = 0; i < fInterfaces.GetCount(); i++ )
fInterfaces[ i ]->RefreshKeyMap();
}
//// WriteKeyMap /////////////////////////////////////////////////////////////
void plInputInterfaceMgr::WriteKeyMap( void )
{
#ifdef PLASMA_EXTERNAL_RELEASE
return;
#endif
FILE* gKeyFile = 0;
gKeyFile = hsFopen( "init\\keyboard.fni", "wt" );
if (gKeyFile)
{
fprintf(gKeyFile, "# To remap a control to a new key,\n");
fprintf(gKeyFile, "# just change the key listed. \n");
fprintf(gKeyFile, "# The list of available commands is at the bottom.\n");
fprintf(gKeyFile, "# For keys with multi-character names \n");
fprintf(gKeyFile, "# like F1 or Backspace, be sure to enter \n");
fprintf(gKeyFile, "# the key name as it appears in the list \n");
fprintf(gKeyFile, "# at the end of the file. \n");
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# Be sure to put quotes around the actual command\n");
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# To add modifiers to a key mapping (like ctrl or shift)\n");
fprintf(gKeyFile, "# append _C for ctrl or _S for Shift (or both)\n");
fprintf(gKeyFile, "# to the actual name of the key.\n");
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# For example, to map Control-Shift-W to Walk Forward\n");
fprintf(gKeyFile, "# your key mapping entry would look like this:\n");
fprintf(gKeyFile, "# Keyboard.BindAction W_C_S \"Walk Forward\"\n");
fprintf(gKeyFile, "# This also works for console command bindings\n");
fprintf(gKeyFile, "# Keyboard.BindAction \t\tKey1\tKey2\t\t\t\tControl\n");
fprintf(gKeyFile, "#\n");
// fprintf(gKeyFile, "Keyboard.ClearBindings\n");
int i;
for( i = 0; i < fInterfaces.GetCount(); i++ )
IWriteNonConsoleCmdKeys( fInterfaces[ i ]->fControlMap, gKeyFile );
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# Console command bindings:\n");
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# Keyboard.BindConsoleCmd \tKey\t\t\tCommand\n");
fprintf(gKeyFile, "#\n");
for( i = 0; i < fInterfaces.GetCount(); i++ )
IWriteConsoleCmdKeys( fInterfaces[ i ]->fControlMap, gKeyFile );
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# Available game commands:\n");
fprintf(gKeyFile, "#\n");
for( int j = 0; plKeyMap::fCmdConvert[ j ].fCode != END_CONTROLS; j++ )
{
if( stricmp( plKeyMap::fCmdConvert[ j ].fDesc, "Run Modifier" ) == 0)
continue;
fprintf( gKeyFile, "# %s\n", plKeyMap::fCmdConvert[ j ].fDesc );
}
fprintf(gKeyFile, "#\n");
fprintf(gKeyFile, "# Key name list (for a-z or 0-9 just use the character)\n");
fprintf(gKeyFile, "#\n");
Win32keyConvert* keyConvert = &plKeyMap::fKeyConversionEnglish[0];
switch (plLocalization::GetLanguage())
{
case plLocalization::kFrench:
keyConvert = &plKeyMap::fKeyConversionFrench[0];
break;
case plLocalization::kGerman:
keyConvert = &plKeyMap::fKeyConversionGerman[0];
break;
//case plLocalization::kSpanish:
// keyConvert = &plKeyMap::fKeyConversionSpanish[0];
// break;
//case plLocalization::kItalian:
// keyConvert = &plKeyMap::fKeyConversionItalian[0];
// break;
// default is English
}
for (i = 0; keyConvert[i].fVKey != 0xffffffff; i++)
{
// if (stricmp(fKeyMap->fKeyConversion[i].fKeyName, "Shift") == 0)
// continue;
fprintf(gKeyFile, "# %s\n", keyConvert[i].fKeyName);
}
fclose(gKeyFile);
}
}
void plInputInterfaceMgr::SetCurrentFocus(plInputInterface *focus)
{
fCurrentFocus = focus;
}
void plInputInterfaceMgr::ReleaseCurrentFocus(plInputInterface *focus)
{
if (fCurrentFocus == focus)
fCurrentFocus = nil;
}
//// IKeyComboToString ///////////////////////////////////////////////////////
// Uses static string, so don't call twice and expect the first result to
// be still valid!
const char *plInputInterfaceMgr::IKeyComboToString( const plKeyCombo &combo )
{
static char str[ 64 ];
bool unmapped = false;
if( combo == plKeyCombo::kUnmapped )
sprintf( str, "(unmapped)" );
else
{
char *c = plKeyMap::ConvertVKeyToChar( combo.fKey );
if( c != nil )
strncpy( str, c, sizeof( str ) );
else
{
if( isalnum( combo.fKey ) )
{
str[ 0 ] = (char)combo.fKey;
str[ 1 ] = 0;
}
else
{
strcpy( str, "(unmapped)" );
unmapped = true;
}
}
if( !unmapped )
{
if( combo.fFlags & plKeyCombo::kCtrl )
strcat( str, "_C" );
if( combo.fFlags & plKeyCombo::kShift )
strcat( str, "_S" );
}
}
return str;
}
//// IWriteNonConsoleCmdKeys /////////////////////////////////////////////////
void plInputInterfaceMgr::IWriteNonConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile )
{
int i;
for( i = 0; i < keyMap->GetNumBindings(); i++ )
{
const plKeyBinding &binding = keyMap->GetBinding( i );
if( binding.GetCode() == B_CONTROL_CONSOLE_COMMAND )
continue;
char key1[ 64 ];
strcpy( key1, IKeyComboToString( binding.GetKey1() ) );
const char *key2 = IKeyComboToString( binding.GetKey2() );
const char *desc = plInputMap::ConvertControlCodeToString( binding.GetCode() );
fprintf( keyFile, "Keyboard.BindAction \t\t%s\t%s\t\t\t\t\"%s\"\n", key1, key2, desc );
}
}
//// IWriteConsoleCmdKeys ////////////////////////////////////////////////////
void plInputInterfaceMgr::IWriteConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile )
{
int i;
for( i = 0; i < keyMap->GetNumBindings(); i++ )
{
const plKeyBinding &binding = keyMap->GetBinding( i );
if( binding.GetCode() != B_CONTROL_CONSOLE_COMMAND )
continue;
// Our bindConsoleCmd console command (echo echo) only takes 1 key combo, not 2,
// so as not to confuse people. Or something. So if we got two bindings, we print
// 2 commands, which is perfectly valid
// if( binding.GetKey1() != plKeyCombo::kUnmapped )
// {
const char *key = IKeyComboToString( binding.GetKey1() );
fprintf( keyFile, "Keyboard.BindConsoleCmd\t%s\t\t\t\"%s\"\n", key, binding.GetExtendedString() );
// }
if( binding.GetKey2() != plKeyCombo::kUnmapped )
{
const char *key = IKeyComboToString( binding.GetKey2() );
fprintf( keyFile, "Keyboard.BindConsoleCmd\t%s\t\t\t\"%s\"\n", key, binding.GetExtendedString() );
}
}
}

View File

@ -0,0 +1,186 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plInputInterfaceMgr.h - The manager of all input interface layers //
// //
//// History /////////////////////////////////////////////////////////////////
// //
// 2.20.02 mcn - Created. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plInputInterfaceMgr_h
#define _plInputInterfaceMgr_h
#include "../pnModifier/plSingleModifier.h"
#include "hsTemplates.h"
#include "hsGeometry3.h"
#include "../pnInputCore/plKeyMap.h"
//// Class Definition ////////////////////////////////////////////////////////
class hsStream;
class hsResMgr;
class plInputInterface;
//class plKeyMap;
enum plKeyDef;
enum ControlEventCode;
class plKey;
class plCtrlCmd;
class plKeyCombo;
class plDefaultKeyCatcher;
class plKeyBinding;
class plInputInterfaceMgr : public plSingleModifier
{
protected:
static plInputInterfaceMgr *fInstance;
hsTArray<plInputInterface *> fInterfaces;
hsTArray<plCtrlCmd *> fMessageQueue;
hsTArray<plKey> fReceivers;
#ifdef MCN_DISABLE_OLD_WITH_NEW_HACK
hsTArray<ControlEventCode> fDisabledCodes;
hsTArray<UInt32> fDisabledKeys;
#endif
hsBool fClickEnabled;
Int32 fCurrentCursor;
hsScalar fCursorOpacity;
hsBool fForceCursorHidden;
Int32 fForceCursorHiddenCount;
plInputInterface *fCurrentFocus;
plDefaultKeyCatcher *fDefaultCatcher;
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty );
void IAddInterface( plInputInterface *iface );
void IRemoveInterface( plInputInterface *iface );
void IUpdateCursor( Int32 newCursor );
hsBool ICheckCursor(plInputInterface *iFace); // returns true if the iface changed cursor settings
void IWriteConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile );
void IWriteNonConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile );
plKeyMap *IGetRoutedKeyMap( ControlEventCode code ); // Null for console commands
void IUnbind( const plKeyCombo &key );
const char *IKeyComboToString( const plKeyCombo &combo );
public:
plInputInterfaceMgr();
virtual ~plInputInterfaceMgr();
CLASSNAME_REGISTER( plInputInterfaceMgr );
GETINTERFACE_ANY( plInputInterfaceMgr, plSingleModifier );
virtual hsBool MsgReceive( plMessage *msg );
virtual void Read( hsStream* s, hsResMgr* mgr );
virtual void Write( hsStream* s, hsResMgr* mgr );
void Init( void );
void Shutdown( void );
void InitDefaultKeyMap( void );
void WriteKeyMap( void );
void RefreshInterfaceKeyMaps( void );
void SetCurrentFocus(plInputInterface *focus);
void ReleaseCurrentFocus(plInputInterface *focus);
void SetDefaultKeyCatcher( plDefaultKeyCatcher *c ) { fDefaultCatcher = c; }
hsBool IsClickEnabled() { return fClickEnabled; }
void ForceCursorHidden( hsBool requestedState );
// Binding routers
void BindAction( const plKeyCombo &key, ControlEventCode code );
void BindAction( const plKeyCombo &key1, const plKeyCombo &key2, ControlEventCode code );
void BindConsoleCmd( const plKeyCombo &key, const char *cmd, plKeyMap::BindPref pref = plKeyMap::kNoPreference );
const plKeyBinding* FindBinding( ControlEventCode code );
const plKeyBinding* FindBindingByConsoleCmd( const char *cmd );
void ClearAllKeyMaps();
void ResetClickableState();
static plInputInterfaceMgr *GetInstance( void ) { return fInstance; }
};
//// plCtrlCmd ///////////////////////////////////////////////////////////////
// Networkable helper class that represents a single control statement
class plCtrlCmd
{
private:
char* fCmd;
plInputInterface *fSource;
public:
plCtrlCmd( plInputInterface *source ) : fCmd(nil),fPct(1.0f), fSource(source) {;}
~plCtrlCmd() { delete [] fCmd; }
const char* GetCmdString() { return fCmd; }
void SetCmdString(const char* cs) { delete [] fCmd; fCmd=hsStrcpy(cs); }
ControlEventCode fControlCode;
hsBool fControlActivated;
hsPoint3 fPt;
hsScalar fPct;
hsBool fNetPropagateToPlayers;
void Read( hsStream* s, hsResMgr* mgr );
void Write( hsStream* s, hsResMgr* mgr );
plInputInterface *GetSource( void ) const { return fSource; }
};
//// Tiny Virtual Class For The Default Key Processor ////////////////////////
//
// Basically, if you want to be the one to catch the leftover key events,
// derive from this class and pass yourself to inputIFaceMgr.
// (it'll auto-tell inputIFaceMgr when it goes away)
//
// Note: if you want to do more than just get the darned key event (like
// mouse events or key bindings or change the cursor or the like), don't do
// this; create your own plInputInterface instead.
class plKeyEventMsg;
class plDefaultKeyCatcher
{
public:
virtual ~plDefaultKeyCatcher();
virtual void HandleKeyEvent( plKeyEventMsg *eventMsg ) = 0;
};
#endif // _plInputInterfaceMgr_h

View File

@ -0,0 +1,693 @@
/*==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/>.
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 "hsConfig.h"
#include "hsWindows.h"
// plInputManager.cpp
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include "hsTypes.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 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( hsScalar 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 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()
{
::ClipCursor(nil);
::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 );
RECT rect;
::GetClientRect(fhWnd,&rect);
::ClientToScreen(fhWnd,(LPPOINT)&rect);
::ClipCursor(&rect);
::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" ,},
};

View File

@ -0,0 +1,96 @@
/*==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/>.
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==*/
// plInputManager.h
#ifndef PL_INPUT_MANAGER_H
#define PL_INPUT_MANAGER_H
#include <windows.h>
#include "hsTypes.h"
#include "hsTemplates.h"
#include "../pnKeyedObject/hsKeyedObject.h"
#include "../pnInputCore/plInputMap.h"
class plDInputMgr;
class plInputDevice;
class plDInputDevice;
class plInputInterfaceMgr;
class plPipeline;
class plInputManager :public hsKeyedObject
{
private:
static hsBool fUseDInput;
public:
plInputManager();
plInputManager( HWND hWnd );
~plInputManager();
CLASSNAME_REGISTER( plInputManager );
GETINTERFACE_ANY( plInputManager, hsKeyedObject );
void AddInputDevice(plInputDevice* pDev);
void InitDInput(HINSTANCE hInst, HWND hWnd);
static void UseDInput(hsBool b) { fUseDInput = b; }
void Update();
static plInputManager* GetInstance() { return fInstance; }
static plInputManager* fInstance;
virtual hsBool MsgReceive(plMessage* msg);
static hsBool RecenterMouse() { return bRecenterMouse > 0; }
static void SetRecenterMouse(hsBool b);
static void RecenterCursor();
void CreateInterfaceMod(plPipeline* p);
void Activate( bool activating );
hsScalar GetMouseScale( void ) const { return fMouseScale; }
void SetMouseScale( hsScalar s );
static plKeyDef UntranslateKey(plKeyDef key, hsBool extended);
protected:
hsTArray<plInputDevice*> fInputDevices;
plDInputMgr* fDInputMgr;
plInputInterfaceMgr *fInterfaceMgr;
bool fActive, fFirstActivated;
hsScalar fMouseScale;
static UInt8 bRecenterMouse;
static HWND fhWnd;
public:
// event handlers
void HandleWin32ControlEvent(UINT message, WPARAM Wparam, LPARAM Lparam, HWND hWnd);
};
// {049DE53E-23A2-4d43-BF68-36AC1B57E357}
static const GUID PL_ACTION_GUID = { 0x49de53e, 0x23a2, 0x4d43, { 0xbf, 0x68, 0x36, 0xac, 0x1b, 0x57, 0xe3, 0x57 } };
#endif

View File

@ -0,0 +1,133 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSceneInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plSceneInputInterface_h
#define _plSceneInputInterface_h
#include "plInputInterface.h"
#include "hsGeometry3.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnUtils/pnUtils.h"
//// Class Definition ////////////////////////////////////////////////////////
class plPipeline;
class plSceneObject;
class plSceneInputInterface : public plInputInterface
{
enum
{
kNotOffering = 0,
kOfferBook,
kBookOffered,
kOfferAccepted,
kOfferLinkPending,
};
protected:
static plSceneInputInterface *fInstance;
UInt32 fCurrentCursor;
UInt8 fButtonState;
hsBool fClickability;
plKey fCurrentClickable, fLastClicked, fCurrentClickableLogicMod;
hsPoint3 fCurrentClickPoint;
hsBool fCurrClickIsAvatar, fLastClickIsAvatar,fFadedLocalAvatar;
hsBool fPendingLink;
int fBookMode; // are we in offer book mode?
plKey fBookKey; // key for the python file modifier for the book we are offering
plKey fOffereeKey;
UInt32 fOffereeID; // ID for the guy who's accepted our link offer
const char* fOfferedAgeFile;
const char* fOfferedAgeInstance;
const char* fSpawnPoint;
Uuid fAgeInstanceGuid;
struct clickableTest
{
clickableTest(plKey k)
{
key = k;
val = false;
}
plKey key;
hsBool val;
};
hsTArray<clickableTest *> fClickableMap;
hsTArray<plKey> fIgnoredAvatars; // these are ignored because they are engaged in avatar-avatar interactions which need to be left undisturbed
hsTArray<plKey> fGUIIgnoredAvatars; // these are ignored because they have a GUI in their face right now
hsTArray<plKey> fLocalIgnoredAvatars; // these are ALL avatars currently in your age. they are ignored when you press the 'ignore' key so you can
// select clickable non-avatar objects through them.
hsPoint3 fLastStartPt, fLastEndPt;
plPipeline *fPipe;
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty );
void IRequestLOSCheck( hsScalar xPos, hsScalar yPos, int ID );
void ISetLastClicked( plKey obj, hsPoint3 hitPoint );
void IHalfFadeAvatar(hsBool out);
hsBool IWorldPosMovedSinceLastLOSCheck( void );
void ClearClickableMap();
void ISendOfferNotification(plKey& offeree, int ID, hsBool net);
void IManageIgnoredAvatars(plKey& offeree, hsBool add);
void ISendAvatarDisabledNotification(hsBool enabled);
void ILinkOffereeToAge();
public:
plSceneInputInterface();
virtual ~plSceneInputInterface();
static hsBool fShowLOS;
// Always return true, since the cursor should be representing how we control the avatar
virtual hsBool HasInterestingCursorID( void ) const { return ( fCurrentCursor != kNullCursor ) ? true : false; }
virtual UInt32 GetPriorityLevel( void ) const { return kSceneInteractionPriority; }
virtual UInt32 GetCurrentCursorID( void ) const {return fCurrentCursor;}
UInt32 SetCurrentCursorID(UInt32 id);
virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg );
void RequestAvatarTurnToPointLOS();
virtual hsBool MsgReceive( plMessage *msg );
virtual void Init( plInputInterfaceMgr *manager );
virtual void Shutdown( void );
virtual void ResetClickableState();
plKey GetCurrMousedAvatar( void ) const { if( fCurrClickIsAvatar ) return fCurrentClickable; else return nil; }
static plSceneInputInterface *GetInstance( void ) { return fInstance; }
};
#endif //_plSceneInputInterface_h

View File

@ -0,0 +1,125 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plTelescopeInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsConfig.h"
#include "hsWindows.h"
#include "hsTypes.h"
#include "plTelescopeInputInterface.h"
#include "plInputInterfaceMgr.h"
#include "plInputManager.h"
#include "plInputDevice.h"
#include "../pnInputCore/plKeyMap.h"
#include "plgDispatch.h"
//// Constructor/Destructor //////////////////////////////////////////////////
plTelescopeInputInterface::plTelescopeInputInterface()
{
SetEnabled( true ); // Always enabled
// Add our control codes to our control map. Do NOT add the key bindings yet.
// Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth.
// This part basically declares us master of the bindings for these commands.
// IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO
// RestoreDefaultKeyMappings()!!!!
fControlMap->AddCode( B_CONTROL_EXIT_MODE, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_PAN_LEFT, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_PAN_RIGHT, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_PAN_UP, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_PAN_DOWN, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_RECENTER, kControlFlagNormal | kControlFlagNoRepeat );
fControlMap->AddCode( B_CAMERA_ZOOM_IN, kControlFlagNormal );
fControlMap->AddCode( B_CAMERA_ZOOM_OUT, kControlFlagNormal );
// IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO
// RestoreDefaultKeyMappings()!!!!
}
plTelescopeInputInterface::~plTelescopeInputInterface()
{
}
//// Init/Shutdown ///////////////////////////////////////////////////////////
void plTelescopeInputInterface::Init( plInputInterfaceMgr *manager )
{
plInputInterface::Init( manager );
}
//// IEval ///////////////////////////////////////////////////////////////////
hsBool plTelescopeInputInterface::IEval( double secs, hsScalar del, UInt32 dirty )
{
return true;
}
//// MsgReceive //////////////////////////////////////////////////////////////
hsBool plTelescopeInputInterface::MsgReceive( plMessage *msg )
{
return false;
}
//// InterpretInputEvent /////////////////////////////////////////////////////
hsBool plTelescopeInputInterface::InterpretInputEvent( plInputEventMsg *pMsg )
{
return false;
}
//// RestoreDefaultKeyMappings ///////////////////////////////////////////////
void plTelescopeInputInterface::RestoreDefaultKeyMappings( void )
{
if( fControlMap == nil )
return;
fControlMap->UnmapAllBindings();
fControlMap->BindKey( KEY_BACKSPACE, B_CONTROL_EXIT_MODE );
fControlMap->BindKey( KEY_NUMPAD5, B_CAMERA_RECENTER );
fControlMap->BindKey( KEY_C, B_CAMERA_RECENTER );
fControlMap->BindKey( KEY_NUMPAD_ADD, B_CAMERA_ZOOM_IN );
fControlMap->BindKey( KEY_NUMPAD_SUBTRACT, B_CAMERA_ZOOM_OUT );
fControlMap->BindKey( KEY_NUMPAD4, B_CAMERA_PAN_LEFT );
fControlMap->BindKey( KEY_NUMPAD6, B_CAMERA_PAN_RIGHT );
fControlMap->BindKey( KEY_NUMPAD8, B_CAMERA_PAN_UP );
fControlMap->BindKey( KEY_NUMPAD2, B_CAMERA_PAN_DOWN );
}

View File

@ -0,0 +1,74 @@
/*==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/>.
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==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plTelescopeInputInterface //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plTelescopeInputInterface_h
#define _plTelescopeInputInterface_h
#include "plInputInterface.h"
#include "../pnKeyedObject/plKey.h"
//// Class Definition ////////////////////////////////////////////////////////
class plTelescopeInputInterface : public plInputInterface
{
protected:
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty );
public:
plTelescopeInputInterface();
virtual ~plTelescopeInputInterface();
virtual void RestoreDefaultKeyMappings( void );
virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg );
virtual hsBool MsgReceive( plMessage *msg );
virtual void Init( plInputInterfaceMgr *manager );
virtual void Shutdown( void ) {;}
// Returns the priority of this interface layer, based on the Priorities enum
virtual UInt32 GetPriorityLevel( void ) const { return kTelescopeInputPriority; }
// Returns the currently active mouse cursor for this layer, as defined in pnMessage/plCursorChangeMsg.h
virtual UInt32 GetCurrentCursorID( void ) const { return kCursorUp; }
// Returns the current opacity that this layer wants the cursor to be, from 0 (xparent) to 1 (opaque)
virtual hsScalar GetCurrentCursorOpacity( void ) const { return 1.f; }
// Returns true if this layer is wanting to change the mouse, false if it isn't interested
virtual hsBool HasInterestingCursorID( void ) const { return false; }
};
#endif //_plTelescopeInputInterface_h