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.

964 lines
29 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// 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 )
{
char* 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() );
}
}
}