You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
948 lines
29 KiB
948 lines
29 KiB
14 years ago
|
/*==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
|
||
|
|
||
14 years ago
|
#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"
|
||
14 years ago
|
|
||
14 years ago
|
#include "pnKeyedObject/plKey.h"
|
||
|
#include "pnKeyedObject/plFixedKey.h"
|
||
14 years ago
|
|
||
14 years ago
|
#include "pnNetCommon/plNetApp.h"
|
||
|
#include "plNetClient/plNetClientMgr.h"
|
||
14 years ago
|
|
||
|
#include "hsResMgr.h"
|
||
|
#include "plgDispatch.h"
|
||
|
#include "plProfile.h"
|
||
14 years ago
|
#include "plResMgr/plLocalization.h"
|
||
14 years ago
|
|
||
|
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() );
|
||
|
}
|
||
|
}
|
||
|
}
|