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.
933 lines
22 KiB
933 lines
22 KiB
4 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/>.
|
||
|
|
||
|
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 "../NucleusLib/inc/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;
|
||
|
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( 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;
|
||
|
}
|
||
|
|