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.
544 lines
14 KiB
544 lines
14 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Implementation of the VGUI ISurface interface using the |
|
// material system to implement it |
|
// |
|
//===========================================================================// |
|
|
|
#if defined( WIN32 ) && !defined( _X360 ) |
|
#include <windows.h> |
|
#include <zmouse.h> |
|
#endif |
|
#include "inputsystem/iinputsystem.h" |
|
#include "tier2/tier2.h" |
|
#include "Input.h" |
|
#include "vguimatsurface.h" |
|
#include "../vgui2/src/VPanel.h" |
|
#include <vgui/KeyCode.h> |
|
#include <vgui/MouseCode.h> |
|
#include <vgui/IVGui.h> |
|
#include <vgui/IPanel.h> |
|
#include <vgui/ISurface.h> |
|
#include <vgui/IClientPanel.h> |
|
#include "inputsystem/ButtonCode.h" |
|
#include "Cursor.h" |
|
#include "tier0/dbg.h" |
|
#include "../vgui2/src/vgui_key_translation.h" |
|
#include <vgui/IInputInternal.h> |
|
#include "tier0/icommandline.h" |
|
#ifdef _X360 |
|
#include "xbox/xbox_win32stubs.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
using namespace vgui; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Vgui input events |
|
//----------------------------------------------------------------------------- |
|
enum VguiInputEventType_t |
|
{ |
|
IE_Close = IE_FirstVguiEvent, |
|
IE_LocateMouseClick, |
|
IE_SetCursor, |
|
IE_KeyTyped, |
|
IE_KeyCodeTyped, |
|
IE_InputLanguageChanged, |
|
IE_IMESetWindow, |
|
IE_IMEStartComposition, |
|
IE_IMEComposition, |
|
IE_IMEEndComposition, |
|
IE_IMEShowCandidates, |
|
IE_IMEChangeCandidates, |
|
IE_IMECloseCandidates, |
|
IE_IMERecomputeModes, |
|
}; |
|
|
|
void InitInput() |
|
{ |
|
EnableInput( true ); |
|
} |
|
|
|
|
|
static bool s_bInputEnabled = true; |
|
#ifdef WIN32 |
|
//----------------------------------------------------------------------------- |
|
// Translates actual keys into VGUI ids |
|
//----------------------------------------------------------------------------- |
|
static WNDPROC s_ChainedWindowProc = NULL; |
|
extern HWND thisWindow; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes the input system |
|
//----------------------------------------------------------------------------- |
|
|
|
static bool s_bIMEComposing = false; |
|
static HWND s_hLastHWnd = 0; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Handles input messages |
|
//----------------------------------------------------------------------------- |
|
static LRESULT CALLBACK MatSurfaceWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) |
|
{ |
|
if ( !s_bInputEnabled ) |
|
goto chainWndProc; |
|
|
|
InputEvent_t event; |
|
memset( &event, 0, sizeof(event) ); |
|
event.m_nTick = g_pInputSystem->GetPollTick(); |
|
|
|
if ( hwnd != s_hLastHWnd ) |
|
{ |
|
s_hLastHWnd = hwnd; |
|
event.m_nType = IE_IMESetWindow; |
|
event.m_nData = (int)s_hLastHWnd; |
|
g_pInputSystem->PostUserEvent( event ); |
|
} |
|
|
|
switch(uMsg) |
|
{ |
|
case WM_QUIT: |
|
// According to windows docs, WM_QUIT should never be passed to wndprocs |
|
Assert( 0 ); |
|
break; |
|
|
|
case WM_CLOSE: |
|
// Handle close messages |
|
{ |
|
LONG_PTR wndProc = GetWindowLongPtrW( hwnd, GWLP_WNDPROC ); |
|
if ( wndProc == (LONG_PTR)MatSurfaceWindowProc ) |
|
{ |
|
event.m_nType = IE_Close; |
|
g_pInputSystem->PostUserEvent( event ); |
|
} |
|
} |
|
return 0; |
|
|
|
// All mouse messages need to mark where the click occurred before chaining down |
|
case WM_LBUTTONDOWN: |
|
case WM_RBUTTONDOWN: |
|
case WM_MBUTTONDOWN: |
|
case MS_WM_XBUTTONDOWN: |
|
case WM_LBUTTONUP: |
|
case WM_RBUTTONUP: |
|
case WM_MBUTTONUP: |
|
case MS_WM_XBUTTONUP: |
|
case WM_LBUTTONDBLCLK: |
|
case WM_RBUTTONDBLCLK: |
|
case WM_MBUTTONDBLCLK: |
|
case MS_WM_XBUTTONDBLCLK: |
|
event.m_nType = IE_LocateMouseClick; |
|
event.m_nData = (short)LOWORD(lParam); |
|
event.m_nData2 = (short)HIWORD(lParam); |
|
g_pInputSystem->PostUserEvent( event ); |
|
break; |
|
|
|
case WM_SETCURSOR: |
|
event.m_nType = IE_SetCursor; |
|
g_pInputSystem->PostUserEvent( event ); |
|
break; |
|
|
|
case WM_XCONTROLLER_KEY: |
|
if ( IsX360() ) |
|
{ |
|
// First have to insert the edge case event |
|
int nRetVal = 0; |
|
if ( s_ChainedWindowProc ) |
|
{ |
|
nRetVal = CallWindowProcW( s_ChainedWindowProc, hwnd, uMsg, wParam, lParam ); |
|
} |
|
|
|
// xboxissue - as yet HL2 input hasn't been made aware of analog inputs or ports |
|
// so just digital step on the sample range |
|
int sample = LOWORD( lParam ); |
|
if ( sample ) |
|
{ |
|
event.m_nType = IE_KeyCodeTyped; |
|
event.m_nData = (vgui::KeyCode)wParam; |
|
g_pInputSystem->PostUserEvent( event ); |
|
} |
|
} |
|
break; |
|
|
|
// Need to deal with key repeat for keydown since inputsystem doesn't |
|
case WM_KEYDOWN: |
|
case WM_SYSKEYDOWN: |
|
{ |
|
// First have to insert the edge case event |
|
int nRetVal = 0; |
|
if ( s_ChainedWindowProc ) |
|
{ |
|
nRetVal = CallWindowProcW( s_ChainedWindowProc, hwnd, uMsg, wParam, lParam ); |
|
} |
|
|
|
int nKeyRepeat = LOWORD( lParam ); |
|
for ( int i = 0; i < nKeyRepeat; ++i ) |
|
{ |
|
event.m_nType = IE_KeyCodeTyped; |
|
event.m_nData = KeyCode_VirtualKeyToVGUI( wParam ); |
|
g_pInputSystem->PostUserEvent( event ); |
|
} |
|
|
|
return nRetVal; |
|
} |
|
|
|
case WM_SYSCHAR: |
|
case WM_CHAR: |
|
if ( !s_bIMEComposing ) |
|
{ |
|
event.m_nType = IE_KeyTyped; |
|
event.m_nData = (int)wParam; |
|
g_pInputSystem->PostUserEvent( event ); |
|
} |
|
break; |
|
|
|
case WM_INPUTLANGCHANGE: |
|
event.m_nType = IE_InputLanguageChanged; |
|
g_pInputSystem->PostUserEvent( event ); |
|
break; |
|
|
|
case WM_IME_STARTCOMPOSITION: |
|
s_bIMEComposing = true; |
|
event.m_nType = IE_IMEStartComposition; |
|
g_pInputSystem->PostUserEvent( event ); |
|
return TRUE; |
|
|
|
case WM_IME_COMPOSITION: |
|
event.m_nType = IE_IMEComposition; |
|
event.m_nData = (int)lParam; |
|
g_pInputSystem->PostUserEvent( event ); |
|
return TRUE; |
|
|
|
case WM_IME_ENDCOMPOSITION: |
|
s_bIMEComposing = false; |
|
event.m_nType = IE_IMEEndComposition; |
|
g_pInputSystem->PostUserEvent( event ); |
|
return TRUE; |
|
|
|
case WM_IME_NOTIFY: |
|
{ |
|
switch (wParam) |
|
{ |
|
default: |
|
break; |
|
|
|
case 14: // Chinese Traditional IMN_PRIVATE... |
|
break; |
|
|
|
case IMN_OPENCANDIDATE: |
|
event.m_nType = IE_IMEShowCandidates; |
|
g_pInputSystem->PostUserEvent( event ); |
|
return 1; |
|
|
|
case IMN_CHANGECANDIDATE: |
|
event.m_nType = IE_IMEChangeCandidates; |
|
g_pInputSystem->PostUserEvent( event ); |
|
return 0; |
|
|
|
case IMN_CLOSECANDIDATE: |
|
event.m_nType = IE_IMECloseCandidates; |
|
g_pInputSystem->PostUserEvent( event ); |
|
break; |
|
|
|
// To detect the change of IME mode, or the toggling of Japanese IME |
|
case IMN_SETCONVERSIONMODE: |
|
case IMN_SETSENTENCEMODE: |
|
case IMN_SETOPENSTATUS: |
|
event.m_nType = IE_IMERecomputeModes; |
|
g_pInputSystem->PostUserEvent( event ); |
|
if ( wParam == IMN_SETOPENSTATUS ) |
|
return 0; |
|
break; |
|
|
|
case IMN_CLOSESTATUSWINDOW: |
|
case IMN_GUIDELINE: |
|
case IMN_OPENSTATUSWINDOW: |
|
case IMN_SETCANDIDATEPOS: |
|
case IMN_SETCOMPOSITIONFONT: |
|
case IMN_SETCOMPOSITIONWINDOW: |
|
case IMN_SETSTATUSWINDOWPOS: |
|
break; |
|
} |
|
} |
|
|
|
case WM_IME_SETCONTEXT: |
|
// We draw all IME windows ourselves |
|
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; |
|
lParam &= ~ISC_SHOWUIGUIDELINE; |
|
lParam &= ~ISC_SHOWUIALLCANDIDATEWINDOW; |
|
break; |
|
|
|
case WM_IME_CHAR: |
|
// We need to process this message so that the IME doesn't double convert the unicode IME characters into garbage characters and post |
|
// them to our window... (get ? marks after text entry ). |
|
return 0; |
|
} |
|
|
|
chainWndProc: |
|
if ( s_ChainedWindowProc ) |
|
return CallWindowProcW( s_ChainedWindowProc, hwnd, uMsg, wParam, lParam ); |
|
|
|
// This means the application is driving the messages (calling our window procedure manually) |
|
// rather than us hooking their window procedure. The engine needs to do this in order for VCR |
|
// mode to play back properly. |
|
return 0; |
|
} |
|
|
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Enables/disables input (enabled by default) |
|
//----------------------------------------------------------------------------- |
|
void EnableInput( bool bEnable ) |
|
{ |
|
#if 0 // #ifdef BENCHMARK |
|
s_bInputEnabled = false; |
|
#else |
|
s_bInputEnabled = bEnable; |
|
#endif |
|
} |
|
|
|
|
|
#ifdef WIN32 |
|
//----------------------------------------------------------------------------- |
|
// Hooks input listening up to a window |
|
//----------------------------------------------------------------------------- |
|
void InputAttachToWindow(void *hwnd) |
|
{ |
|
#if !defined( USE_SDL ) |
|
s_ChainedWindowProc = (WNDPROC)GetWindowLongPtrW( (HWND)hwnd, GWLP_WNDPROC ); |
|
SetWindowLongPtrW( (HWND)hwnd, GWLP_WNDPROC, (LONG_PTR)MatSurfaceWindowProc ); |
|
#endif |
|
} |
|
|
|
void InputDetachFromWindow(void *hwnd) |
|
{ |
|
if (!hwnd) |
|
return; |
|
if ( s_ChainedWindowProc ) |
|
{ |
|
SetWindowLongPtrW( (HWND)hwnd, GWLP_WNDPROC, (LONG_PTR) s_ChainedWindowProc ); |
|
s_ChainedWindowProc = NULL; |
|
} |
|
} |
|
#else |
|
void InputAttachToWindow(void *hwnd) |
|
{ |
|
#if !defined( OSX ) && !defined( LINUX ) |
|
if ( hwnd && !HushAsserts() ) |
|
{ |
|
// under OSX we use the Cocoa mgr to route events rather than hooking winprocs |
|
// and under Linux we use SDL |
|
Assert( !"Implement me" ); |
|
} |
|
#endif |
|
} |
|
|
|
void InputDetachFromWindow(void *hwnd) |
|
{ |
|
#if !defined( OSX ) && !defined( LINUX ) |
|
if ( hwnd && !HushAsserts() ) |
|
{ |
|
// under OSX we use the Cocoa mgr to route events rather than hooking winprocs |
|
// and under Linux we use SDL |
|
Assert( !"Implement me" ); |
|
} |
|
#endif |
|
} |
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Converts an input system button code to a vgui key code |
|
// FIXME: Remove notion of vgui::KeyCode + vgui::MouseCode altogether |
|
//----------------------------------------------------------------------------- |
|
static vgui::KeyCode ButtonCodeToKeyCode( ButtonCode_t buttonCode ) |
|
{ |
|
return ( vgui::KeyCode )buttonCode; |
|
} |
|
|
|
static vgui::MouseCode ButtonCodeToMouseCode( ButtonCode_t buttonCode ) |
|
{ |
|
return ( vgui::MouseCode )buttonCode; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Handles an input event, returns true if the event should be filtered |
|
// from the rest of the game |
|
//----------------------------------------------------------------------------- |
|
bool InputHandleInputEvent( const InputEvent_t &event ) |
|
{ |
|
switch( event.m_nType ) |
|
{ |
|
case IE_ButtonPressed: |
|
{ |
|
// NOTE: data2 is the virtual key code (data1 contains the scan-code one) |
|
ButtonCode_t code = (ButtonCode_t)event.m_nData2; |
|
if ( IsKeyCode( code ) || IsJoystickCode( code ) ) |
|
{ |
|
vgui::KeyCode keyCode = ButtonCodeToKeyCode( code ); |
|
return g_pIInput->InternalKeyCodePressed( keyCode ); |
|
} |
|
|
|
if ( IsJoystickCode( code ) ) |
|
{ |
|
vgui::KeyCode keyCode = ButtonCodeToKeyCode( code ); |
|
return g_pIInput->InternalKeyCodePressed( keyCode ); |
|
} |
|
|
|
if ( IsMouseCode( code ) ) |
|
{ |
|
vgui::MouseCode mouseCode = ButtonCodeToMouseCode( code ); |
|
return g_pIInput->InternalMousePressed( mouseCode ); |
|
} |
|
} |
|
break; |
|
|
|
case IE_ButtonReleased: |
|
{ |
|
// NOTE: data2 is the virtual key code (data1 contains the scan-code one) |
|
ButtonCode_t code = (ButtonCode_t)event.m_nData2; |
|
if ( IsKeyCode( code ) || IsJoystickCode( code ) ) |
|
{ |
|
vgui::KeyCode keyCode = ButtonCodeToKeyCode( code ); |
|
return g_pIInput->InternalKeyCodeReleased( keyCode ); |
|
} |
|
|
|
if ( IsJoystickCode( code ) ) |
|
{ |
|
vgui::KeyCode keyCode = ButtonCodeToKeyCode( code ); |
|
return g_pIInput->InternalKeyCodeReleased( keyCode ); |
|
} |
|
|
|
if ( IsMouseCode( code ) ) |
|
{ |
|
vgui::MouseCode mouseCode = ButtonCodeToMouseCode( code ); |
|
return g_pIInput->InternalMouseReleased( mouseCode ); |
|
} |
|
} |
|
break; |
|
case IE_FingerDown: |
|
{ |
|
//g_pIInput->InternalCursorMoved( event.m_nData2, event.m_nData3 ); |
|
g_pIInput->UpdateCursorPosInternal( event.m_nData2, event.m_nData3 ); |
|
g_pIInput->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED ); |
|
g_pIInput->InternalMousePressed( MOUSE_LEFT ); |
|
} |
|
return true; |
|
case IE_FingerUp: |
|
{ |
|
g_pIInput->UpdateCursorPosInternal( event.m_nData2, event.m_nData3 ); |
|
g_pIInput->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED ); |
|
g_pIInput->InternalMouseReleased( MOUSE_LEFT ); |
|
} |
|
return true; |
|
case IE_FingerMotion: |
|
{ |
|
//g_pIInput->UpdateCursorPosInternal( event.m_nData2, event.m_nData3 ); |
|
g_pIInput->InternalCursorMoved( event.m_nData2, event.m_nData3 ); |
|
} |
|
return true; |
|
case IE_ButtonDoubleClicked: |
|
{ |
|
// NOTE: data2 is the virtual key code (data1 contains the scan-code one) |
|
ButtonCode_t code = (ButtonCode_t)event.m_nData2; |
|
if ( IsMouseCode( code ) ) |
|
{ |
|
vgui::MouseCode mouseCode = ButtonCodeToMouseCode( code ); |
|
return g_pIInput->InternalMouseDoublePressed( mouseCode ); |
|
} |
|
} |
|
break; |
|
|
|
case IE_AnalogValueChanged: |
|
{ |
|
if ( event.m_nData == MOUSE_WHEEL ) |
|
return g_pIInput->InternalMouseWheeled( event.m_nData3 ); |
|
if ( event.m_nData == MOUSE_XY ) |
|
return g_pIInput->InternalCursorMoved( event.m_nData2, event.m_nData3 ); |
|
} |
|
break; |
|
|
|
case IE_KeyCodeTyped: |
|
{ |
|
vgui::KeyCode code = (vgui::KeyCode)event.m_nData; |
|
g_pIInput->InternalKeyCodeTyped( code ); |
|
} |
|
return true; |
|
|
|
case IE_KeyTyped: |
|
{ |
|
vgui::KeyCode code = (vgui::KeyCode)event.m_nData; |
|
g_pIInput->InternalKeyTyped( code ); |
|
} |
|
return true; |
|
|
|
case IE_Quit: |
|
g_pVGui->Stop(); |
|
#if defined( USE_SDL ) |
|
return false; // also let higher layers consume it |
|
#else |
|
return true; |
|
#endif |
|
|
|
case IE_Close: |
|
// FIXME: Change this so we don't stop until 'save' occurs, etc. |
|
g_pVGui->Stop(); |
|
return true; |
|
|
|
case IE_SetCursor: |
|
ActivateCurrentCursor(); |
|
return true; |
|
|
|
case IE_IMESetWindow: |
|
g_pIInput->SetIMEWindow( (void *)event.m_nData ); |
|
return true; |
|
|
|
case IE_LocateMouseClick: |
|
g_pIInput->InternalCursorMoved( event.m_nData, event.m_nData2 ); |
|
return true; |
|
|
|
case IE_InputLanguageChanged: |
|
g_pIInput->OnInputLanguageChanged(); |
|
return true; |
|
|
|
case IE_IMEStartComposition: |
|
g_pIInput->OnIMEStartComposition(); |
|
return true; |
|
|
|
case IE_IMEComposition: |
|
g_pIInput->OnIMEComposition( event.m_nData ); |
|
return true; |
|
|
|
case IE_IMEEndComposition: |
|
g_pIInput->OnIMEEndComposition(); |
|
return true; |
|
|
|
case IE_IMEShowCandidates: |
|
g_pIInput->OnIMEShowCandidates(); |
|
return true; |
|
|
|
case IE_IMEChangeCandidates: |
|
g_pIInput->OnIMEChangeCandidates(); |
|
return true; |
|
|
|
case IE_IMECloseCandidates: |
|
g_pIInput->OnIMECloseCandidates(); |
|
return true; |
|
|
|
case IE_IMERecomputeModes: |
|
g_pIInput->OnIMERecomputeModes(); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|