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.
 
 
 
 
 

779 lines
22 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==*/
// 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 "plGImage/plMipmap.h"
#include "hsWindows.h"
#include "plPipeline.h"
// 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;
plKeyboardDevice::plKeyboardDevice() :
fShiftKeyDown(false),
fCapsLockKeyDown(false),
fAltKeyDown(false),
fCtrlKeyDown(false),
fCapsLockLock(false),
fControlMode(STANDARD_MODE)
{
fInstance = this;
InitKeyboardState();
}
plKeyboardDevice::~plKeyboardDevice()
{
}
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()
{
}
void plKeyboardDevice::HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat, wchar_t c)
{
// update the internal keyboard state
unsigned int keyCode = (unsigned int)key;
if ((key >= 0) && (key < 256))
fKeyboardState[key] = bKeyDown;
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->SetKeyChar( c );
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;
}
else
{
ReleaseAllKeys(); // send key-up events for everything since we're losing focus
}
}
//
//
//
// 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( char* cursor )
{
if( fCursor == nil )
{
plPlateManager::Instance().CreatePlate( &fCursor );
fCursor->CreateFromResource(cursor);
}
else
{
fCursor->ReloadFromResource(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( 2*fCursor->GetMipmap()->GetWidth()/fWidth, 2*fCursor->GetMipmap()->GetHeight()/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(char* 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 );
::ShowCursor( FALSE );
SetCapture(hWnd);
}
else
{
ReleaseCapture();
::ShowCursor( TRUE );
}
}