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

/*==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;
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;
}