mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 14:16:50 +00:00
560 lines
14 KiB
C++
560 lines
14 KiB
C++
//========= 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
|
|
#include "MatSystemSurface.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
using namespace vgui;
|
|
extern CMatSystemSurface g_MatSystemSurface;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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:
|
|
{
|
|
int w,h,x,y; g_MatSystemSurface.GetScreenSize(w, h);
|
|
float _x, _y;
|
|
memcpy( &_x, &event.m_nData2, sizeof(_x) );
|
|
memcpy( &_y, &event.m_nData3, sizeof(_y) );
|
|
x = w*_x; y = h*_y;
|
|
g_pIInput->UpdateCursorPosInternal( x, y );
|
|
g_pIInput->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED );
|
|
g_pIInput->InternalMousePressed( MOUSE_LEFT );
|
|
}
|
|
return true;
|
|
case IE_FingerUp:
|
|
{
|
|
int w,h,x,y; g_MatSystemSurface.GetScreenSize(w, h);
|
|
float _x, _y;
|
|
memcpy( &_x, &event.m_nData2, sizeof(_x) );
|
|
memcpy( &_y, &event.m_nData3, sizeof(_y) );
|
|
x = w*_x; y = h*_y;
|
|
g_pIInput->UpdateCursorPosInternal( x, y );
|
|
g_pIInput->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED );
|
|
g_pIInput->InternalMouseReleased( MOUSE_LEFT );
|
|
}
|
|
return true;
|
|
case IE_FingerMotion:
|
|
{
|
|
int w,h,x,y; g_MatSystemSurface.GetScreenSize(w, h);
|
|
float _x, _y;
|
|
memcpy( &_x, &event.m_nData2, sizeof(_x) );
|
|
memcpy( &_y, &event.m_nData3, sizeof(_y) );
|
|
x = w*_x; y = h*_y;
|
|
g_pIInput->InternalCursorMoved( x, y );
|
|
}
|
|
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 *)(intp)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;
|
|
}
|
|
|
|
|
|
|