diff options
Diffstat (limited to 'vgui2/src/InputWin32.cpp')
| -rw-r--r-- | vgui2/src/InputWin32.cpp | 3198 |
1 files changed, 3198 insertions, 0 deletions
diff --git a/vgui2/src/InputWin32.cpp b/vgui2/src/InputWin32.cpp new file mode 100644 index 0000000..a9a037f --- /dev/null +++ b/vgui2/src/InputWin32.cpp @@ -0,0 +1,3198 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#if defined( WIN32 ) && !defined( _X360 ) +#include <windows.h> +#include <imm.h> +#define DO_IME +#endif + +#include <string.h> + +#include "vgui_internal.h" +#include "VPanel.h" +#include "utlvector.h" +#include <KeyValues.h> +#include "tier0/vcrmode.h" + +#include <vgui/VGUI.h> +#include <vgui/ISystem.h> +#include <vgui/IClientPanel.h> +#include <vgui/IInputInternal.h> +#include <vgui/IPanel.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/KeyCode.h> +#include <vgui/MouseCode.h> +#include "vgui/Cursor.h" +#include <vgui/keyrepeat.h> + +#include "utllinkedlist.h" +#include "tier0/icommandline.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +/* +> Subject: RE: l4d2 & motd +> +> From: Alfred Reynolds +> I'd go with the if it ain't broke don't touch it route, might as well +> leave win32 as is and just knobble the asserts where we know we won't implement it. +> +>> From: Mike Sartain +>> Well now that's interesting. Is it ok to remove it for win32 then? +>> +>>> From: Alfred Reynolds +>>> We never did the IME work, AFAIK it only ever worked on the game's +>>> console in game which isn't useful for users. So, no demand, hard +>>> (actually, really hard) to implement so it wasn't done. +>>> +>>>> From: Mike Sartain +>>>> There are also a bunch of IME Language functions in +>>>> vgui2/src/inputwin32.cpp that are NYI on Linux as well - but it looks +>>>> like those haven't ever been implemented on OSX either. Alfred, what +>>>> is the story there? +*/ +#if 0 // !defined( DO_IME ) && !defined( _X360 ) +#define ASSERT_IF_IME_NYI() Assert( !"IME Support NYI" ) +#else +#define ASSERT_IF_IME_NYI() +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +bool IsDispatchingMessageQueue( void ); + +using namespace vgui; + +class CInputSystem : public IInputInternal +{ +public: + CInputSystem(); + ~CInputSystem(); + + virtual void RunFrame(); + + virtual void PanelDeleted(VPANEL panel); + + virtual void UpdateMouseFocus(int x, int y); + virtual void SetMouseFocus(VPANEL newMouseFocus); + + virtual void SetCursorPos(int x, int y); + virtual void UpdateCursorPosInternal( int x, int y ); + virtual void GetCursorPos(int &x, int &y); + virtual void SetCursorOveride(HCursor cursor); + virtual HCursor GetCursorOveride(); + + + virtual void SetMouseCapture(VPANEL panel); + + virtual VPANEL GetFocus(); + virtual VPANEL GetCalculatedFocus(); + virtual VPANEL GetMouseOver(); + + virtual bool WasMousePressed(MouseCode code); + virtual bool WasMouseDoublePressed(MouseCode code); + virtual bool IsMouseDown(MouseCode code); + virtual bool WasMouseReleased(MouseCode code); + virtual bool WasKeyPressed(KeyCode code); + virtual bool IsKeyDown(KeyCode code); + virtual bool WasKeyTyped(KeyCode code); + virtual bool WasKeyReleased(KeyCode code); + + virtual void GetKeyCodeText(KeyCode code, char *buf, int buflen); + + virtual bool InternalCursorMoved(int x,int y); //expects input in surface space + virtual bool InternalMousePressed(MouseCode code); + virtual bool InternalMouseDoublePressed(MouseCode code); + virtual bool InternalMouseReleased(MouseCode code); + virtual bool InternalMouseWheeled(int delta); + virtual bool InternalKeyCodePressed(KeyCode code); + virtual void InternalKeyCodeTyped(KeyCode code); + virtual void InternalKeyTyped(wchar_t unichar); + virtual bool InternalKeyCodeReleased(KeyCode code); + virtual void SetKeyCodeState( KeyCode code, bool bPressed ); + virtual void SetMouseCodeState( MouseCode code, MouseCodeState_t state ); + virtual void UpdateButtonState( const InputEvent_t &event ); + + virtual VPANEL GetAppModalSurface(); + // set the modal dialog panel. + // all events will go only to this panel and its children. + virtual void SetAppModalSurface(VPANEL panel); + // release the modal dialog panel + // do this when your modal dialog finishes. + virtual void ReleaseAppModalSurface(); + + // returns true if the specified panel is a child of the current modal panel + // if no modal panel is set, then this always returns TRUE + virtual bool IsChildOfModalPanel(VPANEL panel, bool checkModalSubTree = true ); + + // Creates/ destroys "input" contexts, which contains information + // about which controls have mouse + key focus, for example. + virtual HInputContext CreateInputContext(); + virtual void DestroyInputContext( HInputContext context ); + + // Associates a particular panel with an input context + // Associating NULL is valid; it disconnects the panel from the context + virtual void AssociatePanelWithInputContext( HInputContext context, VPANEL pRoot ); + + // Activates a particular input context, use DEFAULT_INPUT_CONTEXT + // to get the one normally used by VGUI + virtual void ActivateInputContext( HInputContext context ); + virtual void PostCursorMessage( ); + virtual void HandleExplicitSetCursor( ); + + virtual void ResetInputContext( HInputContext context ); + + virtual void GetCursorPosition( int &x, int &y ); + + virtual void SetIMEWindow( void *hwnd ); + virtual void *GetIMEWindow(); + + // Change keyboard layout type + virtual void OnChangeIME( bool forward ); + virtual int GetCurrentIMEHandle(); + virtual int GetEnglishIMEHandle(); + + // Returns the Language Bar label (Chinese, Korean, Japanese, Russion, Thai, etc.) + virtual void GetIMELanguageName( wchar_t *buf, int unicodeBufferSizeInBytes ); + // Returns the short code for the language (EN, CH, KO, JP, RU, TH, etc. ). + virtual void GetIMELanguageShortCode( wchar_t *buf, int unicodeBufferSizeInBytes ); + + // Call with NULL dest to get item count + virtual int GetIMELanguageList( LanguageItem *dest, int destcount ); + virtual int GetIMEConversionModes( ConversionModeItem *dest, int destcount ); + virtual int GetIMESentenceModes( SentenceModeItem *dest, int destcount ); + + virtual void OnChangeIMEByHandle( int handleValue ); + virtual void OnChangeIMEConversionModeByHandle( int handleValue ); + virtual void OnChangeIMESentenceModeByHandle( int handleValue ); + + virtual void OnInputLanguageChanged(); + virtual void OnIMEStartComposition(); + virtual void OnIMEComposition( int flags ); + virtual void OnIMEEndComposition(); + + virtual void OnIMEShowCandidates(); + virtual void OnIMEChangeCandidates(); + virtual void OnIMECloseCandidates(); + + virtual void OnIMERecomputeModes(); + + virtual int GetCandidateListCount(); + virtual void GetCandidate( int num, wchar_t *dest, int destSizeBytes ); + virtual int GetCandidateListSelectedItem(); + virtual int GetCandidateListPageSize(); + virtual int GetCandidateListPageStart(); + + virtual void SetCandidateWindowPos( int x, int y ); + virtual bool GetShouldInvertCompositionString(); + virtual bool CandidateListStartsAtOne(); + + virtual void SetCandidateListPageStart( int start ); + + // Passes in a keycode which allows hitting other mouse buttons w/o cancelling capture mode + virtual void SetMouseCaptureEx(VPANEL panel, MouseCode captureStartMouseCode ); + + virtual void RegisterKeyCodeUnhandledListener( VPANEL panel ); + virtual void UnregisterKeyCodeUnhandledListener( VPANEL panel ); + + // Posts unhandled message to all interested panels + virtual void OnKeyCodeUnhandled( int keyCode ); + + // Assumes subTree is a child panel of the root panel for the vgui contect + // if restrictMessagesToSubTree is true, then mouse and kb messages are only routed to the subTree and it's children and mouse/kb focus + // can only be on one of the subTree children, if a mouse click occurs outside of the subtree, and "UnhandledMouseClick" message is sent to unhandledMouseClickListener panel + // if it's set + // if restrictMessagesToSubTree is false, then mouse and kb messages are routed as normal except that they are not routed down into the subtree + // however, if a mouse click occurs outside of the subtree, and "UnhandleMouseClick" message is sent to unhandledMouseClickListener panel + // if it's set + virtual void SetModalSubTree( VPANEL subTree, VPANEL unhandledMouseClickListener, bool restrictMessagesToSubTree = true ); + virtual void ReleaseModalSubTree(); + virtual VPANEL GetModalSubTree(); + + // These toggle whether the modal subtree is exclusively receiving messages or conversely whether it's being excluded from receiving messages + virtual void SetModalSubTreeReceiveMessages( bool state ); + virtual bool ShouldModalSubTreeReceiveMessages() const; + + virtual VPANEL GetMouseCapture(); + + virtual VPANEL GetMouseFocus(); +private: + + VPanel *GetMouseFocusIgnoringModalSubtree(); + + void InternalSetCompositionString( const wchar_t *compstr ); + void InternalShowCandidateWindow(); + void InternalHideCandidateWindow(); + void InternalUpdateCandidateWindow(); + + bool PostKeyMessage(KeyValues *message); + + void DestroyCandidateList(); + void CreateNewCandidateList(); + + VPanel *CalculateNewKeyFocus(); + + void PostModalSubTreeMessage( VPanel *subTree, bool state ); + // returns true if the specified panel is a child of the current modal panel + // if no modal panel is set, then this always returns TRUE + bool IsChildOfModalSubTree(VPANEL panel); + + void SurfaceSetCursorPos( int x, int y ); + void SurfaceGetCursorPos( int &x, int &y ); + + struct InputContext_t + { + VPANEL _rootPanel; + + bool _mousePressed[MOUSE_COUNT]; + bool _mouseDoublePressed[MOUSE_COUNT]; + bool _mouseDown[MOUSE_COUNT]; + bool _mouseReleased[MOUSE_COUNT]; + bool _keyPressed[BUTTON_CODE_COUNT]; + bool _keyTyped[BUTTON_CODE_COUNT]; + bool _keyDown[BUTTON_CODE_COUNT]; + bool _keyReleased[BUTTON_CODE_COUNT]; + + VPanel *_keyFocus; + VPanel *_oldMouseFocus; + VPanel *_mouseFocus; // the panel that has the current mouse focus - same as _mouseOver unless _mouseCapture is set + VPanel *_mouseOver; // the panel that the mouse is currently over, NULL if not over any vgui item + + VPanel *_mouseCapture; // the panel that has currently captured mouse focus + MouseCode m_MouseCaptureStartCode; // The Mouse button which was pressed to initiate mouse capture + VPanel *_appModalPanel; // the modal dialog panel. + + int m_nCursorX; + int m_nCursorY; + + int m_nLastPostedCursorX; + int m_nLastPostedCursorY; + + int m_nExternallySetCursorX; + int m_nExternallySetCursorY; + bool m_bSetCursorExplicitly; + + CUtlVector< VPanel * > m_KeyCodeUnhandledListeners; + + VPanel *m_pModalSubTree; + VPanel *m_pUnhandledMouseClickListener; + bool m_bRestrictMessagesToModalSubTree; + + CKeyRepeatHandler m_keyRepeater; + }; + + void InitInputContext( InputContext_t *pContext ); + InputContext_t *GetInputContext( HInputContext context ); + void PanelDeleted(VPANEL focus, InputContext_t &context); + + HCursor _cursorOverride; + + const char *_keyTrans[KEY_LAST]; + + InputContext_t m_DefaultInputContext; + HInputContext m_hContext; // current input context + + CUtlLinkedList< InputContext_t, HInputContext > m_Contexts; + +#ifdef DO_IME + void *_imeWnd; + CANDIDATELIST *_imeCandidates; +#endif + + int m_nDebugMessages; +}; + +CInputSystem g_Input; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CInputSystem, IInput, VGUI_INPUT_INTERFACE_VERSION, g_Input); // export IInput to everyone else, not IInputInternal! +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CInputSystem, IInputInternal, VGUI_INPUTINTERNAL_INTERFACE_VERSION, g_Input); // for use in external surfaces only! (like the engine surface) + +namespace vgui +{ +vgui::IInputInternal *g_pInput = &g_Input; +} + + +CInputSystem::CInputSystem() +{ + m_nDebugMessages = -1; +#ifdef DO_IME + _imeWnd = null; + _imeCandidates = null; +#endif + InitInputContext( &m_DefaultInputContext ); + m_hContext = DEFAULT_INPUT_CONTEXT; + + // build key to text translation table + // first byte unshifted key + // second byte shifted key + // the rest is the name of the key + _keyTrans[KEY_0] ="0)KEY_0"; + _keyTrans[KEY_1] ="1!KEY_1"; + _keyTrans[KEY_2] ="2@KEY_2"; + _keyTrans[KEY_3] ="3#KEY_3"; + _keyTrans[KEY_4] ="4$KEY_4"; + _keyTrans[KEY_5] ="5%KEY_5"; + _keyTrans[KEY_6] ="6^KEY_6"; + _keyTrans[KEY_7] ="7&KEY_7"; + _keyTrans[KEY_8] ="8*KEY_8"; + _keyTrans[KEY_9] ="9(KEY_9"; + _keyTrans[KEY_A] ="aAKEY_A"; + _keyTrans[KEY_B] ="bBKEY_B"; + _keyTrans[KEY_C] ="cCKEY_C"; + _keyTrans[KEY_D] ="dDKEY_D"; + _keyTrans[KEY_E] ="eEKEY_E"; + _keyTrans[KEY_F] ="fFKEY_F"; + _keyTrans[KEY_G] ="gGKEY_G"; + _keyTrans[KEY_H] ="hHKEY_H"; + _keyTrans[KEY_I] ="iIKEY_I"; + _keyTrans[KEY_J] ="jJKEY_J"; + _keyTrans[KEY_K] ="kKKEY_K"; + _keyTrans[KEY_L] ="lLKEY_L"", L"; + _keyTrans[KEY_M] ="mMKEY_M"; + _keyTrans[KEY_N] ="nNKEY_N"; + _keyTrans[KEY_O] ="oOKEY_O"; + _keyTrans[KEY_P] ="pPKEY_P"; + _keyTrans[KEY_Q] ="qQKEY_Q"; + _keyTrans[KEY_R] ="rRKEY_R"; + _keyTrans[KEY_S] ="sSKEY_S"; + _keyTrans[KEY_T] ="tTKEY_T"; + _keyTrans[KEY_U] ="uUKEY_U"; + _keyTrans[KEY_V] ="vVKEY_V"; + _keyTrans[KEY_W] ="wWKEY_W"; + _keyTrans[KEY_X] ="xXKEY_X"; + _keyTrans[KEY_Y] ="yYKEY_Y"; + _keyTrans[KEY_Z] ="zZKEY_Z"; + _keyTrans[KEY_PAD_0] ="0\0KEY_PAD_0"; + _keyTrans[KEY_PAD_1] ="1\0KEY_PAD_1"; + _keyTrans[KEY_PAD_2] ="2\0KEY_PAD_2"; + _keyTrans[KEY_PAD_3] ="3\0KEY_PAD_3"; + _keyTrans[KEY_PAD_4] ="4\0KEY_PAD_4"; + _keyTrans[KEY_PAD_5] ="5\0KEY_PAD_5"; + _keyTrans[KEY_PAD_6] ="6\0KEY_PAD_6"; + _keyTrans[KEY_PAD_7] ="7\0KEY_PAD_7"; + _keyTrans[KEY_PAD_8] ="8\0KEY_PAD_8"; + _keyTrans[KEY_PAD_9] ="9\0KEY_PAD_9"; + _keyTrans[KEY_PAD_DIVIDE] ="//KEY_PAD_DIVIDE"; + _keyTrans[KEY_PAD_MULTIPLY] ="**KEY_PAD_MULTIPLY"; + _keyTrans[KEY_PAD_MINUS] ="--KEY_PAD_MINUS"; + _keyTrans[KEY_PAD_PLUS] ="++KEY_PAD_PLUS"; + _keyTrans[KEY_PAD_ENTER] ="\0\0KEY_PAD_ENTER"; + _keyTrans[KEY_PAD_DECIMAL] =".\0KEY_PAD_DECIMAL"", L"; + _keyTrans[KEY_LBRACKET] ="[{KEY_LBRACKET"; + _keyTrans[KEY_RBRACKET] ="]}KEY_RBRACKET"; + _keyTrans[KEY_SEMICOLON] =";:KEY_SEMICOLON"; + _keyTrans[KEY_APOSTROPHE] ="'\"KEY_APOSTROPHE"; + _keyTrans[KEY_BACKQUOTE] ="`~KEY_BACKQUOTE"; + _keyTrans[KEY_COMMA] =",<KEY_COMMA"; + _keyTrans[KEY_PERIOD] =".>KEY_PERIOD"; + _keyTrans[KEY_SLASH] ="/?KEY_SLASH"; + _keyTrans[KEY_BACKSLASH] ="\\|KEY_BACKSLASH"; + _keyTrans[KEY_MINUS] ="-_KEY_MINUS"; + _keyTrans[KEY_EQUAL] ="=+KEY_EQUAL"", L"; + _keyTrans[KEY_ENTER] ="\0\0KEY_ENTER"; + _keyTrans[KEY_SPACE] =" KEY_SPACE"; + _keyTrans[KEY_BACKSPACE] ="\0\0KEY_BACKSPACE"; + _keyTrans[KEY_TAB] ="\0\0KEY_TAB"; + _keyTrans[KEY_CAPSLOCK] ="\0\0KEY_CAPSLOCK"; + _keyTrans[KEY_NUMLOCK] ="\0\0KEY_NUMLOCK"; + _keyTrans[KEY_ESCAPE] ="\0\0KEY_ESCAPE"; + _keyTrans[KEY_SCROLLLOCK] ="\0\0KEY_SCROLLLOCK"; + _keyTrans[KEY_INSERT] ="\0\0KEY_INSERT"; + _keyTrans[KEY_DELETE] ="\0\0KEY_DELETE"; + _keyTrans[KEY_HOME] ="\0\0KEY_HOME"; + _keyTrans[KEY_END] ="\0\0KEY_END"; + _keyTrans[KEY_PAGEUP] ="\0\0KEY_PAGEUP"; + _keyTrans[KEY_PAGEDOWN] ="\0\0KEY_PAGEDOWN"; + _keyTrans[KEY_BREAK] ="\0\0KEY_BREAK"; + _keyTrans[KEY_LSHIFT] ="\0\0KEY_LSHIFT"; + _keyTrans[KEY_RSHIFT] ="\0\0KEY_RSHIFT"; + _keyTrans[KEY_LALT] ="\0\0KEY_LALT"; + _keyTrans[KEY_RALT] ="\0\0KEY_RALT"; + _keyTrans[KEY_LCONTROL] ="\0\0KEY_LCONTROL"", L"; + _keyTrans[KEY_RCONTROL] ="\0\0KEY_RCONTROL"", L"; + _keyTrans[KEY_LWIN] ="\0\0KEY_LWIN"; + _keyTrans[KEY_RWIN] ="\0\0KEY_RWIN"; + _keyTrans[KEY_APP] ="\0\0KEY_APP"; + _keyTrans[KEY_UP] ="\0\0KEY_UP"; + _keyTrans[KEY_LEFT] ="\0\0KEY_LEFT"; + _keyTrans[KEY_DOWN] ="\0\0KEY_DOWN"; + _keyTrans[KEY_RIGHT] ="\0\0KEY_RIGHT"; + _keyTrans[KEY_F1] ="\0\0KEY_F1"; + _keyTrans[KEY_F2] ="\0\0KEY_F2"; + _keyTrans[KEY_F3] ="\0\0KEY_F3"; + _keyTrans[KEY_F4] ="\0\0KEY_F4"; + _keyTrans[KEY_F5] ="\0\0KEY_F5"; + _keyTrans[KEY_F6] ="\0\0KEY_F6"; + _keyTrans[KEY_F7] ="\0\0KEY_F7"; + _keyTrans[KEY_F8] ="\0\0KEY_F8"; + _keyTrans[KEY_F9] ="\0\0KEY_F9"; + _keyTrans[KEY_F10] ="\0\0KEY_F10"; + _keyTrans[KEY_F11] ="\0\0KEY_F11"; + _keyTrans[KEY_F12] ="\0\0KEY_F12"; +} + +CInputSystem::~CInputSystem() +{ + DestroyCandidateList(); +} + +//----------------------------------------------------------------------------- +// Resets an input context +//----------------------------------------------------------------------------- +void CInputSystem::InitInputContext( InputContext_t *pContext ) +{ + pContext->_rootPanel = NULL; + pContext->_keyFocus = NULL; + pContext->_oldMouseFocus = NULL; + pContext->_mouseFocus = NULL; + pContext->_mouseOver = NULL; + pContext->_mouseCapture = NULL; + pContext->_appModalPanel = NULL; + + pContext->m_nCursorX = pContext->m_nCursorY = 0; + pContext->m_nLastPostedCursorX = pContext->m_nLastPostedCursorY = -9999; + pContext->m_nExternallySetCursorX = pContext->m_nExternallySetCursorY = 0; + pContext->m_bSetCursorExplicitly = false; + + // zero mouse and keys + memset(pContext->_mousePressed, 0, sizeof(pContext->_mousePressed)); + memset(pContext->_mouseDoublePressed, 0, sizeof(pContext->_mouseDoublePressed)); + memset(pContext->_mouseDown, 0, sizeof(pContext->_mouseDown)); + memset(pContext->_mouseReleased, 0, sizeof(pContext->_mouseReleased)); + memset(pContext->_keyPressed, 0, sizeof(pContext->_keyPressed)); + memset(pContext->_keyTyped, 0, sizeof(pContext->_keyTyped)); + memset(pContext->_keyDown, 0, sizeof(pContext->_keyDown)); + memset(pContext->_keyReleased, 0, sizeof(pContext->_keyReleased)); + + pContext->m_MouseCaptureStartCode = (MouseCode)-1; + + pContext->m_KeyCodeUnhandledListeners.RemoveAll(); + + pContext->m_pModalSubTree = NULL; + pContext->m_pUnhandledMouseClickListener = NULL; + pContext->m_bRestrictMessagesToModalSubTree = false; +} + +void CInputSystem::ResetInputContext( HInputContext context ) +{ + // FIXME: Needs to release various keys, mouse buttons, etc...? + // At least needs to cause things to lose focus + InitInputContext( GetInputContext(context) ); +} + + +//----------------------------------------------------------------------------- +// Creates/ destroys "input" contexts, which contains information +// about which controls have mouse + key focus, for example. +//----------------------------------------------------------------------------- +HInputContext CInputSystem::CreateInputContext() +{ + HInputContext i = m_Contexts.AddToTail(); + InitInputContext( &m_Contexts[i] ); + return i; +} + +void CInputSystem::DestroyInputContext( HInputContext context ) +{ + Assert( context != DEFAULT_INPUT_CONTEXT ); + if ( m_hContext == context ) + { + ActivateInputContext( DEFAULT_INPUT_CONTEXT ); + } + m_Contexts.Remove(context); +} + + +//----------------------------------------------------------------------------- +// Returns the current input context +//----------------------------------------------------------------------------- +CInputSystem::InputContext_t *CInputSystem::GetInputContext( HInputContext context ) +{ + if (context == DEFAULT_INPUT_CONTEXT) + return &m_DefaultInputContext; + return &m_Contexts[context]; +} + + +//----------------------------------------------------------------------------- +// Associates a particular panel with an input context +// Associating NULL is valid; it disconnects the panel from the context +//----------------------------------------------------------------------------- +void CInputSystem::AssociatePanelWithInputContext( HInputContext context, VPANEL pRoot ) +{ + // Changing the root panel should invalidate keysettings, etc. + if (GetInputContext(context)->_rootPanel != pRoot) + { + ResetInputContext( context ); + GetInputContext(context)->_rootPanel = pRoot; + } +} + + +//----------------------------------------------------------------------------- +// Activates a particular input context, use DEFAULT_INPUT_CONTEXT +// to get the one normally used by VGUI +//----------------------------------------------------------------------------- +void CInputSystem::ActivateInputContext( HInputContext context ) +{ + Assert( (context == DEFAULT_INPUT_CONTEXT) || m_Contexts.IsValidIndex(context) ); + m_hContext = context; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInputSystem::RunFrame() +{ + if ( m_nDebugMessages == -1 ) + { + m_nDebugMessages = CommandLine()->FindParm( "-vguifocus" ) ? 1 : 0; + } + + InputContext_t *pContext = GetInputContext(m_hContext); + + // tick whoever has the focus + if (pContext->_keyFocus) + { + // when modal dialogs are up messages only get sent to the dialogs children. + if (IsChildOfModalPanel((VPANEL)pContext->_keyFocus)) + { + g_pIVgui->PostMessage((VPANEL)pContext->_keyFocus, new KeyValues("KeyFocusTicked"), NULL); + } + } + + // tick whoever has the focus + if (pContext->_mouseFocus) + { + // when modal dialogs are up messages only get sent to the dialogs children. + if (IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseFocusTicked"), NULL); + } + } + // Mouse has wandered "off" the modal panel, just force a regular arrow cursor until it wanders back within the proper bounds + else if ( pContext->_appModalPanel ) + { + g_pSurface->SetCursor( vgui::dc_arrow ); + } + + //clear mouse and key states + int i; + for (i = 0; i < MOUSE_COUNT; i++) + { + pContext->_mousePressed[i] = 0; + pContext->_mouseDoublePressed[i] = 0; + pContext->_mouseReleased[i] = 0; + } + for (i = 0; i < BUTTON_CODE_COUNT; i++) + { + pContext->_keyPressed[i] = 0; + pContext->_keyTyped[i] = 0; + pContext->_keyReleased[i] = 0; + } + + VPanel *wantedKeyFocus = CalculateNewKeyFocus(); + + // make sure old and new focus get painted + if (pContext->_keyFocus != wantedKeyFocus) + { + if (pContext->_keyFocus != NULL) + { + pContext->_keyFocus->Client()->InternalFocusChanged(true); + + // there may be out of order operations here, since we're directly calling SendMessage, + // but we need to have focus messages happen immediately, since otherwise mouse events + // happen out of order - more specifically, they happen before the focus changes + + // send a message to the window saying that it's losing focus + { + MEM_ALLOC_CREDIT(); + KeyValues *pMessage = new KeyValues( "KillFocus" ); + KeyValues::AutoDelete autodelete_pMessage( pMessage ); + pMessage->SetPtr( "newPanel", wantedKeyFocus ); + pContext->_keyFocus->SendMessage( pMessage, 0 ); + } + + if ( pContext->_keyFocus ) + { + pContext->_keyFocus->Client()->Repaint(); + } + + // repaint the nearest popup as well, since it will need to redraw after losing focus + VPanel *dlg = pContext->_keyFocus; + while (dlg && !dlg->IsPopup()) + { + dlg = dlg->GetParent(); + } + if (dlg) + { + dlg->Client()->Repaint(); + } + } + if (wantedKeyFocus != NULL) + { + wantedKeyFocus->Client()->InternalFocusChanged(false); + + // there may be out of order operations here, since we're directly calling SendMessage, + // but we need to have focus messages happen immediately, since otherwise mouse events + // happen out of order - more specifically, they happen before the focus changes + + // send a message to the window saying that it's gaining focus + { + MEM_ALLOC_CREDIT(); + KeyValues *pMsg = new KeyValues("SetFocus"); + KeyValues::AutoDelete autodelete_pMsg( pMsg ); + wantedKeyFocus->SendMessage( pMsg, 0 ); + } + wantedKeyFocus->Client()->Repaint(); + + // repaint the nearest popup as well, since it will need to redraw after gaining focus + VPanel *dlg = wantedKeyFocus; + while (dlg && !dlg->IsPopup()) + { + dlg = dlg->GetParent(); + } + if (dlg) + { + dlg->Client()->Repaint(); + } + } + + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "changing kb focus from %s to %s\n", + pContext->_keyFocus ? pContext->_keyFocus->GetName() : "(no name)", + wantedKeyFocus ? wantedKeyFocus->GetName() : "(no name)" ); + } + + // accept the focus request + pContext->_keyFocus = wantedKeyFocus; + if (pContext->_keyFocus) + { + pContext->_keyFocus->MoveToFront(); + } + } + + // Pump any key repeats + KeyCode repeatCode = pContext->m_keyRepeater.KeyRepeated(); + if (repeatCode) + { + InternalKeyCodePressed( repeatCode ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Calculate the new key focus +//----------------------------------------------------------------------------- +VPanel *CInputSystem::CalculateNewKeyFocus() +{ + InputContext_t *pContext = GetInputContext(m_hContext); + + // get the top-order panel + VPanel *wantedKeyFocus = NULL; + + VPanel *pRoot = (VPanel *)pContext->_rootPanel; + VPanel *top = pRoot; + if ( g_pSurface->GetPopupCount() > 0 ) + { + // find the highest-level window that is both visible and a popup + int nIndex = g_pSurface->GetPopupCount(); + + while ( nIndex ) + { + top = (VPanel *)g_pSurface->GetPopup( --nIndex ); + + // traverse the hierarchy and check if the popup really is visible + if (top && + // top->IsPopup() && // These are right out of of the popups list!!! + top->IsVisible() && + top->IsKeyBoardInputEnabled() && + !g_pSurface->IsMinimized((VPANEL)top) && + IsChildOfModalSubTree( (VPANEL)top ) && + (!pRoot || top->HasParent( pRoot )) ) + { + bool bIsVisible = top->IsVisible(); + VPanel *p = top->GetParent(); + // drill down the hierarchy checking that everything is visible + while(p && bIsVisible) + { + if( p->IsVisible()==false) + { + bIsVisible = false; + break; + } + p=p->GetParent(); + } + + if ( bIsVisible && !g_pSurface->IsMinimized( (VPANEL)top ) ) + break; + } + + top = pRoot; + } + } + + if (top) + { + // ask the top-level panel for what it considers to be the current focus + wantedKeyFocus = (VPanel *)top->Client()->GetCurrentKeyFocus(); + if (!wantedKeyFocus) + { + wantedKeyFocus = top; + } + } + + // check to see if any of this surfaces panels have the focus + if (!g_pSurface->HasFocus()) + { + wantedKeyFocus=NULL; + } + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel((VPANEL)wantedKeyFocus)) + { + wantedKeyFocus=NULL; + } + + return wantedKeyFocus; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CInputSystem::PanelDeleted(VPANEL vfocus, InputContext_t &context) +{ + VPanel *focus = (VPanel *)vfocus; + if (context._keyFocus == focus) + { + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "removing kb focus %s\n", + context._keyFocus ? context._keyFocus->GetName() : "(no name)" ); + } + context._keyFocus = NULL; + } + if (context._mouseOver == focus) + { + /* + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "removing kb focus %s\n", + context._keyFocus ? pcontext._keyFocus->GetName() : "(no name)" ); + } + */ + context._mouseOver = NULL; + } + if (context._oldMouseFocus == focus) + { + context._oldMouseFocus = NULL; + } + if (context._mouseFocus == focus) + { + context._mouseFocus = NULL; + } + + // NOTE: These two will only ever happen for the default context at the moment + if (context._mouseCapture == focus) + { + SetMouseCapture(NULL); + context._mouseCapture = NULL; + } + if (context._appModalPanel == focus) + { + ReleaseAppModalSurface(); + } + if ( context.m_pUnhandledMouseClickListener == focus ) + { + context.m_pUnhandledMouseClickListener = NULL; + } + if ( context.m_pModalSubTree == focus ) + { + context.m_pModalSubTree = NULL; + context.m_bRestrictMessagesToModalSubTree = false; + } + + context.m_KeyCodeUnhandledListeners.FindAndRemove( focus ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *focus - +//----------------------------------------------------------------------------- +void CInputSystem::PanelDeleted(VPANEL focus) +{ + HInputContext i; + for (i = m_Contexts.Head(); i != m_Contexts.InvalidIndex(); i = m_Contexts.Next(i) ) + { + PanelDeleted( focus, m_Contexts[i] ); + } + PanelDeleted( focus, m_DefaultInputContext ); +} + + + + +//----------------------------------------------------------------------------- +// Purpose: Sets the new mouse focus +// won't override _mouseCapture settings +// Input : newMouseFocus - +//----------------------------------------------------------------------------- +void CInputSystem::SetMouseFocus(VPANEL newMouseFocus) +{ + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel(newMouseFocus)) + { + return; + } + + bool wantsMouse, isPopup; // = popup->GetMouseInput(); + VPanel *panel = (VPanel *)newMouseFocus; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + wantsMouse = false; + if ( newMouseFocus ) + { + do + { + wantsMouse = panel->IsMouseInputEnabled(); + isPopup = panel->IsPopup(); + panel = panel->GetParent(); + } + while ( wantsMouse && !isPopup && panel && panel->GetParent() ); // only consider panels that want mouse input + } + + // if this panel doesn't want mouse input don't let it get focus + if (newMouseFocus && !wantsMouse) + { + return; + } + + if ((VPANEL)pContext->_mouseOver != newMouseFocus || (!pContext->_mouseCapture && (VPANEL)pContext->_mouseFocus != newMouseFocus) ) + { + pContext->_oldMouseFocus = pContext->_mouseOver; + pContext->_mouseOver = (VPanel *)newMouseFocus; + + //tell the old panel with the mouseFocus that the cursor exited + if ( pContext->_oldMouseFocus != NULL ) + { + // only notify of entry if the mouse is not captured or we're the captured panel + if ( !pContext->_mouseCapture || pContext->_oldMouseFocus == pContext->_mouseCapture ) + { + g_pIVgui->PostMessage( (VPANEL)pContext->_oldMouseFocus, new KeyValues( "CursorExited" ), NULL ); + } + } + + //tell the new panel with the mouseFocus that the cursor entered + if ( pContext->_mouseOver != NULL ) + { + // only notify of entry if the mouse is not captured or we're the captured panel + if ( !pContext->_mouseCapture || pContext->_mouseOver == pContext->_mouseCapture ) + { + g_pIVgui->PostMessage( (VPANEL)pContext->_mouseOver, new KeyValues( "CursorEntered" ), NULL ); + } + } + + // set where the mouse is currently over + // mouse capture overrides destination + VPanel *newFocus = pContext->_mouseCapture ? pContext->_mouseCapture : pContext->_mouseOver; + + if ( m_nDebugMessages > 0 ) + { + g_pIVgui->DPrintf2( "changing mouse focus from %s to %s\n", + pContext->_mouseFocus ? pContext->_mouseFocus->GetName() : "(no name)", + newFocus ? newFocus->GetName() : "(no name)" ); + } + + + pContext->_mouseFocus = newFocus; + } +} + +VPanel *CInputSystem::GetMouseFocusIgnoringModalSubtree() +{ + // find the panel that has the focus + VPanel *focus = NULL; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + int x, y; + x = pContext->m_nCursorX; + y = pContext->m_nCursorY; + + if (!pContext->_rootPanel) + { + if (g_pSurface->IsCursorVisible() && g_pSurface->IsWithin(x, y)) + { + // faster version of code below + // checks through each popup in order, top to bottom windows + for (int i = g_pSurface->GetPopupCount() - 1; i >= 0; i--) + { + VPanel *popup = (VPanel *)g_pSurface->GetPopup(i); + VPanel *panel = popup; + bool wantsMouse = panel->IsMouseInputEnabled(); + bool isVisible = !g_pSurface->IsMinimized((VPANEL)panel); + + while ( isVisible && panel && panel->GetParent() ) // only consider panels that want mouse input + { + isVisible = panel->IsVisible(); + panel = panel->GetParent(); + } + + + if ( wantsMouse && isVisible ) + { + focus = (VPanel *)popup->Client()->IsWithinTraverse(x, y, false); + if (focus) + break; + } + } + if (!focus) + { + focus = (VPanel *)((VPanel *)g_pSurface->GetEmbeddedPanel())->Client()->IsWithinTraverse(x, y, false); + } + } + } + else + { + focus = (VPanel *)((VPanel *)(pContext->_rootPanel))->Client()->IsWithinTraverse(x, y, false); + } + + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if ( !IsChildOfModalPanel((VPANEL)focus, false )) + { + // should this be _appModalPanel? + focus = NULL; + } + + return focus; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Calculates which panel the cursor is currently over and sets it up +// as the current mouse focus. +//----------------------------------------------------------------------------- +void CInputSystem::UpdateMouseFocus(int x, int y) +{ + // find the panel that has the focus + VPanel *focus = NULL; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + if (g_pSurface->IsCursorVisible() && g_pSurface->IsWithin(x, y)) + { + // faster version of code below + // checks through each popup in order, top to bottom windows + int c = g_pSurface->GetPopupCount(); + for (int i = c - 1; i >= 0; i--) + { + VPanel *popup = (VPanel *)g_pSurface->GetPopup(i); + VPanel *panel = popup; + + if ( pContext->_rootPanel && !popup->HasParent((VPanel*)pContext->_rootPanel) ) + { + // if we have a root panel, only consider popups that belong to it + continue; + } +#if defined( _DEBUG ) + char const *pchName = popup->GetName(); + NOTE_UNUSED( pchName ); +#endif + bool wantsMouse = panel->IsMouseInputEnabled() && IsChildOfModalSubTree( (VPANEL)panel ); + if ( !wantsMouse ) + continue; + + bool isVisible = !g_pSurface->IsMinimized((VPANEL)panel); + if ( !isVisible ) + continue; + + while ( isVisible && panel && panel->GetParent() ) // only consider panels that want mouse input + { + isVisible = panel->IsVisible(); + panel = panel->GetParent(); + } + + + if ( !wantsMouse || !isVisible ) + continue; + + focus = (VPanel *)popup->Client()->IsWithinTraverse(x, y, false); + if (focus) + break; + } + if (!focus) + { + focus = (VPanel *)((VPanel *)g_pSurface->GetEmbeddedPanel())->Client()->IsWithinTraverse(x, y, false); + } + } + + // mouse focus debugging code + /* + static VPanel *oldFocus = (VPanel *)0x0001; + if (oldFocus != focus) + { + oldFocus = focus; + if (focus) + { + g_pIVgui->DPrintf2("mouse over: (%s, %s)\n", focus->GetName(), focus->GetClassName()); + } + else + { + g_pIVgui->DPrintf2("mouse over: (NULL)\n"); + } + } + */ + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel((VPANEL)focus)) + { + // should this be _appModalPanel? + focus = NULL; + } + + SetMouseFocus((VPANEL)focus); +} + +// Passes in a keycode which allows hitting other mouse buttons w/o cancelling capture mode +void CInputSystem::SetMouseCaptureEx(VPANEL panel, MouseCode captureStartMouseCode ) +{ + // This sets m_MouseCaptureStartCode to -1, so we set the real value afterward + SetMouseCapture( panel ); + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel(panel)) + { + return; + } + + InputContext_t *pContext = GetInputContext( m_hContext ); + Assert( pContext ); + pContext->m_MouseCaptureStartCode = captureStartMouseCode; +} + +VPANEL CInputSystem::GetMouseCapture() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + return (VPANEL)pContext->_mouseCapture; +} + +//----------------------------------------------------------------------------- +// Purpose: Sets or releases the mouse capture +// Input : panel - pointer to the panel to get mouse capture +// a NULL panel means that you want to clear the mouseCapture +// MouseCaptureLost is sent to the panel that loses the mouse capture +//----------------------------------------------------------------------------- +void CInputSystem::SetMouseCapture(VPANEL panel) +{ + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (!IsChildOfModalPanel(panel)) + { + return; + } + + InputContext_t *pContext = GetInputContext( m_hContext ); + Assert( pContext ); + + pContext->m_MouseCaptureStartCode = (MouseCode)-1; + + // send a message if the panel is losing mouse capture + if (pContext->_mouseCapture && panel != (VPANEL)pContext->_mouseCapture) + { + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MouseCaptureLost"), NULL); + } + + if (panel == NULL) + { + if (pContext->_mouseCapture != NULL) + { + g_pSurface->EnableMouseCapture((VPANEL)pContext->_mouseCapture, false); + } + } + else + { + g_pSurface->EnableMouseCapture(panel, true); + } + + pContext->_mouseCapture = (VPanel *)panel; +} + +// returns true if the specified panel is a child of the current modal panel +// if no modal panel is set, then this always returns TRUE +bool CInputSystem::IsChildOfModalSubTree(VPANEL panel) +{ + if ( !panel ) + return true; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext->m_pModalSubTree ) + { + // If panel is child of modal subtree, the allow messages to route to it if restrict messages is set + bool isChildOfModal = ((VPanel *)panel)->HasParent(pContext->m_pModalSubTree ); + if ( isChildOfModal ) + { + return pContext->m_bRestrictMessagesToModalSubTree; + } + // If panel is not a child of modal subtree, then only allow messages if we're not restricting them to the modal subtree + else + { + return !pContext->m_bRestrictMessagesToModalSubTree; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: check if we are in modal state, +// and if we are make sure this panel has the modal panel as a parent +//----------------------------------------------------------------------------- +bool CInputSystem::IsChildOfModalPanel(VPANEL panel, bool checkModalSubTree /*= true*/ ) +{ + // NULL is ok. + if (!panel) + return true; + + InputContext_t *pContext = GetInputContext( m_hContext ); + + // if we are in modal state, make sure this panel is a child of us. + if (pContext->_appModalPanel) + { + if (!((VPanel *)panel)->HasParent(pContext->_appModalPanel)) + { + return false; + } + } + + if ( !checkModalSubTree ) + return true; + + // Defer to modal subtree logic instead... + return IsChildOfModalSubTree( panel ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CInputSystem::GetFocus() +{ + return (VPANEL)( GetInputContext( m_hContext )->_keyFocus ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CInputSystem::GetCalculatedFocus() +{ + return (VPANEL) CalculateNewKeyFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +VPANEL CInputSystem::GetMouseOver() +{ + return (VPANEL)( GetInputContext( m_hContext )->_mouseOver ); +} + +VPANEL CInputSystem::GetMouseFocus() +{ + return (VPANEL)( GetInputContext( m_hContext )->_mouseFocus ); +} + +bool CInputSystem::WasMousePressed( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mousePressed[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::WasMouseDoublePressed( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mouseDoublePressed[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::IsMouseDown( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mouseDown[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::WasMouseReleased( MouseCode code ) +{ + return GetInputContext( m_hContext )->_mouseReleased[ code - MOUSE_FIRST ]; +} + +bool CInputSystem::WasKeyPressed( KeyCode code ) +{ + return GetInputContext( m_hContext )->_keyPressed[ code - KEY_FIRST ]; +} + +bool CInputSystem::IsKeyDown( KeyCode code ) +{ + return GetInputContext( m_hContext )->_keyDown[ code - KEY_FIRST ]; +} + +bool CInputSystem::WasKeyTyped( KeyCode code ) +{ + return GetInputContext( m_hContext )->_keyTyped[ code - KEY_FIRST ]; +} + +bool CInputSystem::WasKeyReleased( KeyCode code ) +{ + // changed from: only return true if the key was released and the passed in panel matches the keyFocus + return GetInputContext( m_hContext )->_keyReleased[ code - KEY_FIRST ]; +} + + +//----------------------------------------------------------------------------- +// Cursor position; this is the current position read from the input queue. +// We need to set it because client code may read this during Mouse Pressed +// events, etc. +//----------------------------------------------------------------------------- +void CInputSystem::UpdateCursorPosInternal( int x, int y ) +{ + // Windows sends a CursorMoved message even when you haven't actually + // moved the cursor, this means we are going into this fxn just by clicking + // in the window. We only want to execute this code if we have actually moved + // the cursor while dragging. So this code has been added to check + // if we have actually moved from our previous position. + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext->m_nCursorX == x && pContext->m_nCursorY == y ) + return; + + pContext->m_nCursorX = x; + pContext->m_nCursorY = y; + + // Cursor has moved, so make sure the mouseFocus is current + UpdateMouseFocus( x, y ); +} + + +//----------------------------------------------------------------------------- +// This is called by panels to teleport the cursor +//----------------------------------------------------------------------------- +void CInputSystem::SetCursorPos( int x, int y ) +{ + if ( IsDispatchingMessageQueue() ) + { + InputContext_t *pContext = GetInputContext( m_hContext ); + pContext->m_nExternallySetCursorX = x; + pContext->m_nExternallySetCursorY = y; + pContext->m_bSetCursorExplicitly = true; + } + else + { + SurfaceSetCursorPos( x, y ); + } +} + + +void CInputSystem::GetCursorPos(int &x, int &y) +{ + if ( IsDispatchingMessageQueue() ) + { + GetCursorPosition( x, y ); + } + else + { + SurfaceGetCursorPos( x, y ); + } +} + + +// Here for backward compat +void CInputSystem::GetCursorPosition( int &x, int &y ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + x = pContext->m_nCursorX; + y = pContext->m_nCursorY; +} + +//----------------------------------------------------------------------------- +// Purpose: Converts a key code into a full key name +//----------------------------------------------------------------------------- +void CInputSystem::GetKeyCodeText(KeyCode code, char *buf, int buflen) +{ + if (!buf) + return; + + // copy text into buf up to buflen in length + // skip 2 in _keyTrans because the first two are for GetKeyCodeChar + for (int i = 0; i < buflen; i++) + { + char ch = _keyTrans[code][i+2]; + buf[i] = ch; + if (ch == 0) + break; + } + +} + + +//----------------------------------------------------------------------------- +// Low-level cursor getting/setting functions +//----------------------------------------------------------------------------- +void CInputSystem::SurfaceSetCursorPos(int x, int y) +{ + if ( g_pSurface->HasCursorPosFunctions() ) // does the surface export cursor functions for us to use? + { + g_pSurface->SurfaceSetCursorPos(x,y); + } + else + { + // translate into coordinates relative to surface + int px, py, pw, pt; + g_pSurface->GetAbsoluteWindowBounds(px, py, pw, pt); + x += px; + y += py; + // set windows cursor pos +#ifdef WIN32 + ::SetCursorPos(x, y); +#else + // From Alfred on 8/15/2012. + // For l4d2, the vguimatsurface/cursor.cpp functions fire in the engine, the vgui2 ones + // should be dormant (this isn't true for Steam however). + // + // If we ever do need to implement this, look at SDL_GetMouseState(), etc. + Assert( !"CInputSystem::SurfaceSetCursorPos NYI" ); +#endif + } +} + +void CInputSystem::SurfaceGetCursorPos( int &x, int &y ) +{ +#ifndef _X360 // X360TBD + if ( g_pSurface->HasCursorPosFunctions() ) // does the surface export cursor functions for us to use? + { + g_pSurface->SurfaceGetCursorPos( x,y ); + } + else + { +#ifdef WIN32 + // get mouse position in windows + POINT pnt; + VCRHook_GetCursorPos(&pnt); + x = pnt.x; + y = pnt.y; + + // translate into coordinates relative to surface + int px, py, pw, pt; + g_pSurface->GetAbsoluteWindowBounds(px, py, pw, pt); + x -= px; + y -= py; +#else + // From Alfred on 8/15/2012. + // For l4d2, the vguimatsurface/cursor.cpp functions fire in the engine, the vgui2 ones + // should be dormant (this isn't true for Steam however). + Assert( !"CInputSystem::SurfaceGetCursorPos NYI" ); + x = 0; + y = 0; +#endif + } +#else + x = 0; + y = 0; +#endif +} + +void CInputSystem::SetCursorOveride(HCursor cursor) +{ + _cursorOverride = cursor; +} + +HCursor CInputSystem::GetCursorOveride() +{ + return _cursorOverride; +} + + +//----------------------------------------------------------------------------- +// Called when we've detected cursor has moved via a windows message +//----------------------------------------------------------------------------- +bool CInputSystem::InternalCursorMoved(int x, int y) +{ + g_pIVgui->PostMessage((VPANEL) MESSAGE_CURSOR_POS, new KeyValues("SetCursorPosInternal", "xpos", x, "ypos", y), NULL); + return true; +} + + +//----------------------------------------------------------------------------- +// Makes sure the windows cursor is in the right place after processing input +//----------------------------------------------------------------------------- +void CInputSystem::HandleExplicitSetCursor( ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + if ( pContext->m_bSetCursorExplicitly ) + { + pContext->m_nCursorX = pContext->m_nExternallySetCursorX; + pContext->m_nCursorY = pContext->m_nExternallySetCursorY; + pContext->m_bSetCursorExplicitly = false; + + // NOTE: This forces a cursor moved message to be posted next time + pContext->m_nLastPostedCursorX = pContext->m_nLastPostedCursorY = -9999; + + SurfaceSetCursorPos( pContext->m_nCursorX, pContext->m_nCursorY ); + UpdateMouseFocus( pContext->m_nCursorX, pContext->m_nCursorY ); + } +} + + +//----------------------------------------------------------------------------- +// Called when we've detected cursor has moved via a windows message +//----------------------------------------------------------------------------- +void CInputSystem::PostCursorMessage( ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + if ( pContext->m_bSetCursorExplicitly ) + { + // NOTE m_bSetCursorExplicitly will be reset to false in HandleExplicitSetCursor + pContext->m_nCursorX = pContext->m_nExternallySetCursorX; + pContext->m_nCursorY = pContext->m_nExternallySetCursorY; + } + + if ( pContext->m_nLastPostedCursorX == pContext->m_nCursorX && pContext->m_nLastPostedCursorY == pContext->m_nCursorY ) + return; + + pContext->m_nLastPostedCursorX = pContext->m_nCursorX; + pContext->m_nLastPostedCursorY = pContext->m_nCursorY; + + if ( pContext->_mouseCapture ) + { + if (!IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + return; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("CursorMoved", "xpos", pContext->m_nCursorX, "ypos", pContext->m_nCursorY), NULL); + } + else if (pContext->_mouseFocus != NULL) + { + // mouse focus is current from UpdateMouse focus + // so the appmodal check has already been made. + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("CursorMoved", "xpos", pContext->m_nCursorX, "ypos", pContext->m_nCursorY), NULL); + } +} + +bool CInputSystem::InternalMousePressed(MouseCode code) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + VPanel *pTargetPanel = pContext->_mouseOver; + if ( pContext->_mouseCapture && IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + bFilter = true; + + bool captureLost = code == pContext->m_MouseCaptureStartCode || pContext->m_MouseCaptureStartCode == (MouseCode)-1; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MousePressed", "code", code), NULL); + pTargetPanel = pContext->_mouseCapture; + + if ( captureLost ) + { + // this has to happen after MousePressed so the panel doesn't Think it got a mouse press after it lost capture + SetMouseCapture(NULL); + } + } + else if ( (pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus) ) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + bFilter = true; + + // tell the panel with the mouseFocus that the mouse was presssed + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MousePressed", "code", code), NULL); +// g_pIVgui->DPrintf2("MousePressed: (%s, %s)\n", _mouseFocus->GetName(), _mouseFocus->GetClassName()); + pTargetPanel = pContext->_mouseFocus; + } + else if ( pContext->m_pModalSubTree && pContext->m_pUnhandledMouseClickListener ) + { + VPanel *p = GetMouseFocusIgnoringModalSubtree(); + if ( p ) + { + bool isChildOfModal = IsChildOfModalSubTree( (VPANEL)p ); + bool isUnRestricted = !pContext->m_bRestrictMessagesToModalSubTree; + + if ( isUnRestricted != isChildOfModal ) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + g_pIVgui->PostMessage( ( VPANEL )pContext->m_pUnhandledMouseClickListener, new KeyValues( "UnhandledMouseClick", "code", code ), NULL ); + pTargetPanel = pContext->m_pUnhandledMouseClickListener; + bFilter = true; + } + } + } + + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if ( IsChildOfModalPanel( (VPANEL)pTargetPanel ) ) + { + g_pSurface->SetTopLevelFocus( (VPANEL)pTargetPanel ); + } + + return bFilter; +} + +bool CInputSystem::InternalMouseDoublePressed(MouseCode code) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + VPanel *pTargetPanel = pContext->_mouseOver; + if ( pContext->_mouseCapture && IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MouseDoublePressed", "code", code), NULL); + pTargetPanel = pContext->_mouseCapture; + bFilter = true; + } + else if ( (pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + // tell the panel with the mouseFocus that the mouse was double presssed + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseDoublePressed", "code", code), NULL); + pTargetPanel = pContext->_mouseFocus; + bFilter = true; + } + + // check if we are in modal state, + // and if we are make sure this panel is a child of us. + if (IsChildOfModalPanel((VPANEL)pTargetPanel)) + { + g_pSurface->SetTopLevelFocus((VPANEL)pTargetPanel); + } + + return bFilter; +} + +bool CInputSystem::InternalMouseReleased( MouseCode code ) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if (pContext->_mouseCapture && IsChildOfModalPanel((VPANEL)pContext->_mouseCapture)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + // the panel with mouse capture gets all messages + g_pIVgui->PostMessage((VPANEL)pContext->_mouseCapture, new KeyValues("MouseReleased", "code", code), NULL ); + bFilter = true; + } + else if ((pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + // The faked mouse wheel button messages are specifically ignored by vgui + if ( code == MOUSE_WHEEL_DOWN || code == MOUSE_WHEEL_UP ) + return true; + + //tell the panel with the mouseFocus that the mouse was release + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseReleased", "code", code), NULL ); + bFilter = true; + } + + return bFilter; +} + +bool CInputSystem::InternalMouseWheeled(int delta) +{ + // True means we've processed the message and other code shouldn't see this message + bool bFilter = false; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if ((pContext->_mouseFocus != NULL) && IsChildOfModalPanel((VPANEL)pContext->_mouseFocus)) + { + // the mouseWheel works with the mouseFocus, not the keyFocus + g_pIVgui->PostMessage((VPANEL)pContext->_mouseFocus, new KeyValues("MouseWheeled", "delta", delta), NULL); + bFilter = true; + } + return bFilter; +} + +//----------------------------------------------------------------------------- +// Updates the internal key/mouse state associated with the current input context without sending messages +//----------------------------------------------------------------------------- +void CInputSystem::SetMouseCodeState( MouseCode code, MouseCodeState_t state ) +{ + if ( !IsMouseCode( code ) ) + return; + + InputContext_t *pContext = GetInputContext( m_hContext ); + switch( state ) + { + case BUTTON_RELEASED: + pContext->_mouseReleased[ code - MOUSE_FIRST ] = 1; + break; + + case BUTTON_PRESSED: + pContext->_mousePressed[ code - MOUSE_FIRST ] = 1; + break; + + case BUTTON_DOUBLECLICKED: + pContext->_mouseDoublePressed[ code - MOUSE_FIRST ] = 1; + break; + } + + pContext->_mouseDown[ code - MOUSE_FIRST ] = ( state != BUTTON_RELEASED ); +} + +void CInputSystem::SetKeyCodeState( KeyCode code, bool bPressed ) +{ + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return; + + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( bPressed ) + { + //set key state + pContext->_keyPressed[ code - KEY_FIRST ] = 1; + } + else + { + // set key state + pContext->_keyReleased[ code - KEY_FIRST ] = 1; + } + pContext->_keyDown[ code - KEY_FIRST ] = bPressed; +} + +void CInputSystem::UpdateButtonState( const InputEvent_t &event ) +{ + switch( event.m_nType ) + { + case IE_ButtonPressed: + case IE_ButtonReleased: + case IE_ButtonDoubleClicked: + { + // NOTE: data2 is the virtual key code (data1 contains the scan-code one) + ButtonCode_t code = (ButtonCode_t)event.m_nData2; + + // FIXME: Workaround hack + if ( IsKeyCode( code ) || IsJoystickCode( code ) ) + { + SetKeyCodeState( code, ( event.m_nType != IE_ButtonReleased ) ); + break; + } + + if ( IsMouseCode( code ) ) + { + MouseCodeState_t state; + state = ( event.m_nType == IE_ButtonReleased ) ? vgui::BUTTON_RELEASED : vgui::BUTTON_PRESSED; + if ( event.m_nType == IE_ButtonDoubleClicked ) + { + state = vgui::BUTTON_DOUBLECLICKED; + } + + SetMouseCodeState( code, state ); + break; + } + } + break; + } +} + +bool CInputSystem::InternalKeyCodePressed( KeyCode code ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + // mask out bogus keys + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return false; + + bool bFilter = PostKeyMessage( new KeyValues("KeyCodePressed", "code", code ) ); + if ( bFilter ) + { + // Only notice the key down for repeating if we actually used the key + pContext->m_keyRepeater.KeyDown( code ); + } + return bFilter; +} + +void CInputSystem::InternalKeyCodeTyped( KeyCode code ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + // mask out bogus keys + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return; + + // set key state + pContext->_keyTyped[ code - KEY_FIRST ] = 1; + + // tell the current focused panel that a key was typed + PostKeyMessage(new KeyValues("KeyCodeTyped", "code", code)); +} + +void CInputSystem::InternalKeyTyped(wchar_t unichar) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + // set key state + if( unichar <= KEY_LAST ) + { + pContext->_keyTyped[unichar]=1; + } + + // tell the current focused panel that a key was typed + PostKeyMessage(new KeyValues("KeyTyped", "unichar", unichar)); +} + +bool CInputSystem::InternalKeyCodeReleased( KeyCode code ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + + // mask out bogus keys + if ( !IsKeyCode( code ) && !IsJoystickCode( code ) ) + return false; + + pContext->m_keyRepeater.KeyUp( code ); + + return PostKeyMessage(new KeyValues("KeyCodeReleased", "code", code)); +} + +//----------------------------------------------------------------------------- +// Purpose: posts a message to the key focus if it's valid +//----------------------------------------------------------------------------- +bool CInputSystem::PostKeyMessage(KeyValues *message) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if( (pContext->_keyFocus!= NULL) && IsChildOfModalPanel((VPANEL)pContext->_keyFocus)) + { +#ifdef _X360 + g_pIVgui->PostMessage((VPANEL) MESSAGE_CURRENT_KEYFOCUS, message, NULL ); +#else + //tell the current focused panel that a key was released + g_pIVgui->PostMessage((VPANEL)pContext->_keyFocus, message, NULL ); +#endif + return true; + } + + message->deleteThis(); + return false; +} + +VPANEL CInputSystem::GetAppModalSurface() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + return (VPANEL)pContext->_appModalPanel; +} + +void CInputSystem::SetAppModalSurface(VPANEL panel) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + pContext->_appModalPanel = (VPanel *)panel; +} + + +void CInputSystem::ReleaseAppModalSurface() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + pContext->_appModalPanel = NULL; +} + + +#ifdef DO_IME + +enum LANGFLAG +{ + ENGLISH, + TRADITIONAL_CHINESE, + JAPANESE, + KOREAN, + SIMPLIFIED_CHINESE, + UNKNOWN, + + NUM_IMES_SUPPORTED +} LangFlag; + +struct LanguageIds +{ + // char const *idname; + unsigned short id; + int languageflag; + wchar_t const *shortcode; + wchar_t const *displayname; + bool invertcomposition; +}; + +LanguageIds g_LanguageIds[] = +{ + { 0x0000, UNKNOWN, L"", L"Neutral" }, + { 0x007f, UNKNOWN, L"", L"Invariant" }, + { 0x0400, UNKNOWN, L"", L"User Default Language" }, + { 0x0800, UNKNOWN, L"", L"System Default Language" }, + { 0x0436, UNKNOWN, L"AF", L"Afrikaans" }, + { 0x041c, UNKNOWN, L"SQ", L"Albanian" }, + { 0x0401, UNKNOWN, L"AR", L"Arabic (Saudi Arabia)" }, + { 0x0801, UNKNOWN, L"AR", L"Arabic (Iraq)" }, + { 0x0c01, UNKNOWN, L"AR", L"Arabic (Egypt)" }, + { 0x1001, UNKNOWN, L"AR", L"Arabic (Libya)" }, + { 0x1401, UNKNOWN, L"AR", L"Arabic (Algeria)" }, + { 0x1801, UNKNOWN, L"AR", L"Arabic (Morocco)" }, + { 0x1c01, UNKNOWN, L"AR", L"Arabic (Tunisia)" }, + { 0x2001, UNKNOWN, L"AR", L"Arabic (Oman)" }, + { 0x2401, UNKNOWN, L"AR", L"Arabic (Yemen)" }, + { 0x2801, UNKNOWN, L"AR", L"Arabic (Syria)" }, + { 0x2c01, UNKNOWN, L"AR", L"Arabic (Jordan)" }, + { 0x3001, UNKNOWN, L"AR", L"Arabic (Lebanon)" }, + { 0x3401, UNKNOWN, L"AR", L"Arabic (Kuwait)" }, + { 0x3801, UNKNOWN, L"AR", L"Arabic (U.A.E.)" }, + { 0x3c01, UNKNOWN, L"AR", L"Arabic (Bahrain)" }, + { 0x4001, UNKNOWN, L"AR", L"Arabic (Qatar)" }, + { 0x042b, UNKNOWN, L"HY", L"Armenian" }, + { 0x042c, UNKNOWN, L"AZ", L"Azeri (Latin)" }, + { 0x082c, UNKNOWN, L"AZ", L"Azeri (Cyrillic)" }, + { 0x042d, UNKNOWN, L"ES", L"Basque" }, + { 0x0423, UNKNOWN, L"BE", L"Belarusian" }, + { 0x0445, UNKNOWN, L"", L"Bengali (India)" }, + { 0x141a, UNKNOWN, L"", L"Bosnian (Bosnia and Herzegovina)" }, + { 0x0402, UNKNOWN, L"BG", L"Bulgarian" }, + { 0x0455, UNKNOWN, L"", L"Burmese" }, + { 0x0403, UNKNOWN, L"CA", L"Catalan" }, + { 0x0404, TRADITIONAL_CHINESE, L"CHT", L"#IME_0404", true }, + { 0x0804, SIMPLIFIED_CHINESE, L"CHS", L"#IME_0804", true }, + { 0x0c04, UNKNOWN, L"CH", L"Chinese (Hong Kong SAR, PRC)" }, + { 0x1004, UNKNOWN, L"CH", L"Chinese (Singapore)" }, + { 0x1404, UNKNOWN, L"CH", L"Chinese (Macao SAR)" }, + { 0x041a, UNKNOWN, L"HR", L"Croatian" }, + { 0x101a, UNKNOWN, L"HR", L"Croatian (Bosnia and Herzegovina)" }, + { 0x0405, UNKNOWN, L"CZ", L"Czech" }, + { 0x0406, UNKNOWN, L"DK", L"Danish" }, + { 0x0465, UNKNOWN, L"MV", L"Divehi" }, + { 0x0413, UNKNOWN, L"NL", L"Dutch (Netherlands)" }, + { 0x0813, UNKNOWN, L"BE", L"Dutch (Belgium)" }, + { 0x0409, ENGLISH, L"EN", L"#IME_0409" }, + { 0x0809, ENGLISH, L"EN", L"English (United Kingdom)" }, + { 0x0c09, ENGLISH, L"EN", L"English (Australian)" }, + { 0x1009, ENGLISH, L"EN", L"English (Canadian)" }, + { 0x1409, ENGLISH, L"EN", L"English (New Zealand)" }, + { 0x1809, ENGLISH, L"EN", L"English (Ireland)" }, + { 0x1c09, ENGLISH, L"EN", L"English (South Africa)" }, + { 0x2009, ENGLISH, L"EN", L"English (Jamaica)" }, + { 0x2409, ENGLISH, L"EN", L"English (Caribbean)" }, + { 0x2809, ENGLISH, L"EN", L"English (Belize)" }, + { 0x2c09, ENGLISH, L"EN", L"English (Trinidad)" }, + { 0x3009, ENGLISH, L"EN", L"English (Zimbabwe)" }, + { 0x3409, ENGLISH, L"EN", L"English (Philippines)" }, + { 0x0425, UNKNOWN, L"ET", L"Estonian" }, + { 0x0438, UNKNOWN, L"FO", L"Faeroese" }, + { 0x0429, UNKNOWN, L"FA", L"Farsi" }, + { 0x040b, UNKNOWN, L"FI", L"Finnish" }, + { 0x040c, UNKNOWN, L"FR", L"#IME_040c" }, + { 0x080c, UNKNOWN, L"FR", L"French (Belgian)" }, + { 0x0c0c, UNKNOWN, L"FR", L"French (Canadian)" }, + { 0x100c, UNKNOWN, L"FR", L"French (Switzerland)" }, + { 0x140c, UNKNOWN, L"FR", L"French (Luxembourg)" }, + { 0x180c, UNKNOWN, L"FR", L"French (Monaco)" }, + { 0x0456, UNKNOWN, L"GL", L"Galician" }, + { 0x0437, UNKNOWN, L"KA", L"Georgian" }, + { 0x0407, UNKNOWN, L"DE", L"#IME_0407" }, + { 0x0807, UNKNOWN, L"DE", L"German (Switzerland)" }, + { 0x0c07, UNKNOWN, L"DE", L"German (Austria)" }, + { 0x1007, UNKNOWN, L"DE", L"German (Luxembourg)" }, + { 0x1407, UNKNOWN, L"DE", L"German (Liechtenstein)" }, + { 0x0408, UNKNOWN, L"GR", L"Greek" }, + { 0x0447, UNKNOWN, L"IN", L"Gujarati" }, + { 0x040d, UNKNOWN, L"HE", L"Hebrew" }, + { 0x0439, UNKNOWN, L"HI", L"Hindi" }, + { 0x040e, UNKNOWN, L"HU", L"Hungarian" }, + { 0x040f, UNKNOWN, L"IS", L"Icelandic" }, + { 0x0421, UNKNOWN, L"ID", L"Indonesian" }, + { 0x0434, UNKNOWN, L"", L"isiXhosa/Xhosa (South Africa)" }, + { 0x0435, UNKNOWN, L"", L"isiZulu/Zulu (South Africa)" }, + { 0x0410, UNKNOWN, L"IT", L"#IME_0410" }, + { 0x0810, UNKNOWN, L"IT", L"Italian (Switzerland)" }, + { 0x0411, JAPANESE, L"JP", L"#IME_0411" }, + { 0x044b, UNKNOWN, L"IN", L"Kannada" }, + { 0x0457, UNKNOWN, L"IN", L"Konkani" }, + { 0x0412, KOREAN, L"KR", L"#IME_0412" }, + { 0x0812, UNKNOWN, L"KR", L"Korean (Johab)" }, + { 0x0440, UNKNOWN, L"KZ", L"Kyrgyz." }, + { 0x0426, UNKNOWN, L"LV", L"Latvian" }, + { 0x0427, UNKNOWN, L"LT", L"Lithuanian" }, + { 0x0827, UNKNOWN, L"LT", L"Lithuanian (Classic)" }, + { 0x042f, UNKNOWN, L"MK", L"FYRO Macedonian" }, + { 0x043e, UNKNOWN, L"MY", L"Malay (Malaysian)" }, + { 0x083e, UNKNOWN, L"MY", L"Malay (Brunei Darussalam)" }, + { 0x044c, UNKNOWN, L"IN", L"Malayalam (India)" }, + { 0x0481, UNKNOWN, L"", L"Maori (New Zealand)" }, + { 0x043a, UNKNOWN, L"", L"Maltese (Malta)" }, + { 0x044e, UNKNOWN, L"IN", L"Marathi" }, + { 0x0450, UNKNOWN, L"MN", L"Mongolian" }, + { 0x0414, UNKNOWN, L"NO", L"Norwegian (Bokmal)" }, + { 0x0814, UNKNOWN, L"NO", L"Norwegian (Nynorsk)" }, + { 0x0415, UNKNOWN, L"PL", L"Polish" }, + { 0x0416, UNKNOWN, L"PT", L"Portuguese (Brazil)" }, + { 0x0816, UNKNOWN, L"PT", L"Portuguese (Portugal)" }, + { 0x0446, UNKNOWN, L"IN", L"Punjabi" }, + { 0x046b, UNKNOWN, L"", L"Quechua (Bolivia)" }, + { 0x086b, UNKNOWN, L"", L"Quechua (Ecuador)" }, + { 0x0c6b, UNKNOWN, L"", L"Quechua (Peru)" }, + { 0x0418, UNKNOWN, L"RO", L"Romanian" }, + { 0x0419, UNKNOWN, L"RU", L"#IME_0419" }, + { 0x044f, UNKNOWN, L"IN", L"Sanskrit" }, + { 0x043b, UNKNOWN, L"", L"Sami, Northern (Norway)" }, + { 0x083b, UNKNOWN, L"", L"Sami, Northern (Sweden)" }, + { 0x0c3b, UNKNOWN, L"", L"Sami, Northern (Finland)" }, + { 0x103b, UNKNOWN, L"", L"Sami, Lule (Norway)" }, + { 0x143b, UNKNOWN, L"", L"Sami, Lule (Sweden)" }, + { 0x183b, UNKNOWN, L"", L"Sami, Southern (Norway)" }, + { 0x1c3b, UNKNOWN, L"", L"Sami, Southern (Sweden)" }, + { 0x203b, UNKNOWN, L"", L"Sami, Skolt (Finland)" }, + { 0x243b, UNKNOWN, L"", L"Sami, Inari (Finland)" }, + { 0x0c1a, UNKNOWN, L"SR", L"Serbian (Cyrillic)" }, + { 0x1c1a, UNKNOWN, L"SR", L"Serbian (Cyrillic, Bosnia, and Herzegovina)" }, + { 0x081a, UNKNOWN, L"SR", L"Serbian (Latin)" }, + { 0x181a, UNKNOWN, L"SR", L"Serbian (Latin, Bosnia, and Herzegovina)" }, + { 0x046c, UNKNOWN, L"", L"Sesotho sa Leboa/Northern Sotho (South Africa)" }, + { 0x0432, UNKNOWN, L"", L"Setswana/Tswana (South Africa)" }, + { 0x041b, UNKNOWN, L"SK", L"Slovak" }, + { 0x0424, UNKNOWN, L"SI", L"Slovenian" }, + { 0x040a, UNKNOWN, L"ES", L"#IME_040a" }, + { 0x080a, UNKNOWN, L"ES", L"Spanish (Mexican)" }, + { 0x0c0a, UNKNOWN, L"ES", L"Spanish (Spain, Modern Sort)" }, + { 0x100a, UNKNOWN, L"ES", L"Spanish (Guatemala)" }, + { 0x140a, UNKNOWN, L"ES", L"Spanish (Costa Rica)" }, + { 0x180a, UNKNOWN, L"ES", L"Spanish (Panama)" }, + { 0x1c0a, UNKNOWN, L"ES", L"Spanish (Dominican Republic)" }, + { 0x200a, UNKNOWN, L"ES", L"Spanish (Venezuela)" }, + { 0x240a, UNKNOWN, L"ES", L"Spanish (Colombia)" }, + { 0x280a, UNKNOWN, L"ES", L"Spanish (Peru)" }, + { 0x2c0a, UNKNOWN, L"ES", L"Spanish (Argentina)" }, + { 0x300a, UNKNOWN, L"ES", L"Spanish (Ecuador)" }, + { 0x340a, UNKNOWN, L"ES", L"Spanish (Chile)" }, + { 0x380a, UNKNOWN, L"ES", L"Spanish (Uruguay)" }, + { 0x3c0a, UNKNOWN, L"ES", L"Spanish (Paraguay)" }, + { 0x400a, UNKNOWN, L"ES", L"Spanish (Bolivia)" }, + { 0x440a, UNKNOWN, L"ES", L"Spanish (El Salvador)" }, + { 0x480a, UNKNOWN, L"ES", L"Spanish (Honduras)" }, + { 0x4c0a, UNKNOWN, L"ES", L"Spanish (Nicaragua)" }, + { 0x500a, UNKNOWN, L"ES", L"Spanish (Puerto Rico)" }, + { 0x0430, UNKNOWN, L"", L"Sutu" }, + { 0x0441, UNKNOWN, L"KE", L"Swahili (Kenya)" }, + { 0x041d, UNKNOWN, L"SV", L"Swedish" }, + { 0x081d, UNKNOWN, L"SV", L"Swedish (Finland)" }, + { 0x045a, UNKNOWN, L"SY", L"Syriac" }, + { 0x0449, UNKNOWN, L"IN", L"Tamil" }, + { 0x0444, UNKNOWN, L"RU", L"Tatar (Tatarstan)" }, + { 0x044a, UNKNOWN, L"IN", L"Telugu" }, + { 0x041e, UNKNOWN, L"TH", L"#IME_041e" }, + { 0x041f, UNKNOWN, L"TR", L"Turkish" }, + { 0x0422, UNKNOWN, L"UA", L"Ukrainian" }, + { 0x0420, UNKNOWN, L"PK", L"Urdu (Pakistan)" }, + { 0x0820, UNKNOWN, L"IN", L"Urdu (India)" }, + { 0x0443, UNKNOWN, L"UZ", L"Uzbek (Latin)" }, + { 0x0843, UNKNOWN, L"UZ", L"Uzbek (Cyrillic)" }, + { 0x042a, UNKNOWN, L"VN", L"Vietnamese" }, + { 0x0452, UNKNOWN, L"", L"Welsh (United Kingdom)" }, +}; + +static LanguageIds *GetLanguageInfo( unsigned short id ) +{ + for ( int j = 0; j < sizeof( g_LanguageIds ) / sizeof( g_LanguageIds[ 0 ] ); ++j ) + { + if ( g_LanguageIds[ j ].id == id ) + { + return &g_LanguageIds[ j ]; + break; + } + } + return NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// CIMEDlg message handlers +static bool IsIDInList( unsigned short id, int count, HKL *list ) +{ + for ( int i = 0; i < count; ++i ) + { + if ( LOWORD( list[ i ] ) == id ) + { + return true; + } + } + return false; +} + +static const wchar_t *GetLanguageName( unsigned short id ) +{ + wchar_t const *name = L"???"; + for ( int j = 0; j < sizeof( g_LanguageIds ) / sizeof( g_LanguageIds[ 0 ] ); ++j ) + { + if ( g_LanguageIds[ j ].id == id ) + { + name = g_LanguageIds[ j ].displayname; + break; + } + } + return name; +} + +#endif // DO_IME + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *hwnd - +//----------------------------------------------------------------------------- +void CInputSystem::SetIMEWindow( void *hwnd ) +{ +#ifdef DO_IME + _imeWnd = hwnd; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void *CInputSystem::GetIMEWindow() +{ +#ifdef DO_IME + return _imeWnd; +#else + return NULL; +#endif +} + +#ifdef DO_IME +static void SpewIMEInfo( int langid ) +{ + LanguageIds *info = GetLanguageInfo( langid ); + if ( info ) + { + wchar_t const *name = info->shortcode ? info->shortcode : L"???"; + wchar_t outstr[ 512 ]; + V_swprintf_safe( outstr, L"IME language changed to: %s", name ); + OutputDebugStringW( outstr ); + OutputDebugStringW( L"\n" ); + } +} +#endif // DO_IME + +// Change keyboard layout type +void CInputSystem::OnChangeIME( bool forward ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HKL currentKb = GetKeyboardLayout( 0 ); + + UINT numKBs = GetKeyboardLayoutList( 0, NULL ); + if ( numKBs > 0 ) + { + HKL *list = new HKL[ numKBs ]; + + GetKeyboardLayoutList( numKBs, list ); + + int oldKb = 0; + CUtlVector< HKL > selections; + + for ( unsigned int i = 0; i < numKBs; ++i ) + { + BOOL first = !IsIDInList( LOWORD( list[ i ] ), i, list ); + + if ( !first ) + continue; + + selections.AddToTail( list[ i ] ); + if ( list[ i ] == currentKb ) + { + oldKb = selections.Count() - 1; + } + } + + oldKb += forward ? 1 : -1; + if ( oldKb < 0 ) + { + oldKb = max( 0, selections.Count() - 1 ); + } + else if ( oldKb >= selections.Count() ) + { + oldKb = 0; + } + + ActivateKeyboardLayout( selections[ oldKb ], 0 ); + + int langid = LOWORD( selections[ oldKb ] ); + SpewIMEInfo( langid ); + + delete[] list; + } +#endif +} + +int CInputSystem::GetCurrentIMEHandle() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HKL hkl = (HKL)GetKeyboardLayout( 0 ); + return (int)hkl; +#else + return 0; +#endif +} + +int CInputSystem::GetEnglishIMEHandle() +{ +#ifdef DO_IME + HKL hkl = (HKL)0x04090409; + return (int)hkl; +#else + return 0; +#endif +} + +void CInputSystem::OnChangeIMEByHandle( int handleValue ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HKL hkl = (HKL)handleValue; + + ActivateKeyboardLayout( hkl, 0 ); + + int langid = LOWORD( hkl); + + SpewIMEInfo( langid ); +#endif +} + + // Returns the Language Bar label (Chinese, Korean, Japanese, Russion, Thai, etc.) +void CInputSystem::GetIMELanguageName( wchar_t *buf, int unicodeBufferSizeInBytes ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + wchar_t const *name = GetLanguageName( LOWORD( GetKeyboardLayout( 0 ) ) ); + wcsncpy( buf, name, unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ); + buf[ unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ] = L'\0'; +#else + buf[0] = L'\0'; +#endif +} + // Returns the short code for the language (EN, CH, KO, JP, RU, TH, etc. ). +void CInputSystem::GetIMELanguageShortCode( wchar_t *buf, int unicodeBufferSizeInBytes ) +{ +#ifdef DO_IME + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + if ( !info ) + { + buf[ 0 ] = L'\0'; + } + else + { + wcsncpy( buf, info->shortcode, unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ); + buf[ unicodeBufferSizeInBytes / sizeof( wchar_t ) - 1 ] = L'\0'; + } +#else + buf[0] = L'\0'; +#endif +} + +// Call with NULL dest to get item count +int CInputSystem::GetIMELanguageList( LanguageItem *dest, int destcount ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + int iret = 0; + + UINT numKBs = GetKeyboardLayoutList( 0, NULL ); + if ( numKBs > 0 ) + { + HKL *list = new HKL[ numKBs ]; + + GetKeyboardLayoutList( numKBs, list ); + + CUtlVector< HKL > selections; + + for ( unsigned int i = 0; i < numKBs; ++i ) + { + BOOL first = !IsIDInList( LOWORD( list[ i ] ), i, list ); + + if ( !first ) + continue; + + selections.AddToTail( list[ i ] ); + } + + iret = selections.Count(); + if ( dest ) + { + for ( int i = 0; i < min(iret,destcount); ++i ) + { + HKL hkl = selections[ i ]; + + IInput::LanguageItem *p = &dest[ i ]; + + LanguageIds *info = GetLanguageInfo( LOWORD( hkl ) ); + + memset( p, 0, sizeof( IInput::LanguageItem ) ); + + wcsncpy( p->shortname, info->shortcode, sizeof( p->shortname ) / sizeof( wchar_t ) ); + p->shortname[ sizeof( p->shortname ) / sizeof( wchar_t ) - 1 ] = L'\0'; + + wcsncpy( p->menuname, info->displayname, sizeof( p->menuname ) / sizeof( wchar_t ) ); + p->menuname[ sizeof( p->menuname ) / sizeof( wchar_t ) - 1 ] = L'\0'; + + p->handleValue = (int)hkl; + p->active = ( hkl == GetKeyboardLayout( 0 ) ) ? true : false; + } + } + + delete[] list; + } + return iret; +#else + return 0; +#endif +} + +/* +// Flag for effective options in conversion mode +BOOL fConvMode[NUM_IMES_SUPPORTED][13] = +{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // EN + {1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0}, // Trad CH + {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1}, // Japanese + {1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // Kor + {1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0}, // Simp CH + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // UNK(same as EN) +} + +// Flag for effective options in sentence mode +BOOL fSentMode[NUM_IMES_SUPPORTED][6] = +{ + {0, 0, 0, 0, 0, 0}, // EN + {0, 1, 0, 0, 0, 0}, // Trad CH + {1, 1, 1, 1, 1, 1}, // Japanese + {0, 0, 0, 0, 0, 0}, // Kor + {0, 0, 0, 0, 0, 0} // Simp CH + {0, 0, 0, 0, 0, 0}, // UNK(same as EN) +}; + +// Conversion mode message +DWORD dwConvModeMsg[13] = { + IME_CMODE_ALPHANUMERIC, IME_CMODE_NATIVE, IME_CMODE_KATAKANA, + IME_CMODE_LANGUAGE, IME_CMODE_FULLSHAPE, IME_CMODE_ROMAN, + IME_CMODE_CHARCODE, IME_CMODE_HANJACONVERT, IME_CMODE_SOFTKBD, + IME_CMODE_NOCONVERSION, IME_CMODE_EUDC, IME_CMODE_SYMBOL, + IME_CMODE_FIXED}; + +// Sentence mode message +DWORD dwSentModeMsg[6] = { + IME_SMODE_NONE, IME_SMODE_PLAURALCLAUSE, IME_SMODE_SINGLECONVERT, + IME_SMODE_AUTOMATIC, IME_SMODE_PHRASEPREDICT, IME_SMODE_CONVERSATION }; + +// ENGLISH, +// TRADITIONAL_CHINESE, +// JAPANESE, +// KOREAN, +// SIMPLIFIED_CHINESE, +// UNKNOWN, +*/ + +#ifdef DO_IME + +struct IMESettingsTransform +{ + IMESettingsTransform( unsigned int cmr, unsigned int cma, unsigned int smr, unsigned int sma ) : + cmode_remove( cmr ), + cmode_add( cma ), + smode_remove( smr ), + smode_add( sma ) + { + } + + void Apply( HWND hwnd ) + { + HIMC hImc = ImmGetContext( hwnd ); + if ( hImc ) + { + DWORD dwConvMode, dwSentMode; + + ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode ); + + dwConvMode &= ~cmode_remove; + dwSentMode &= ~smode_remove; + + ImmSetConversionStatus( hImc, dwConvMode, dwSentMode ); + + dwConvMode |= cmode_add; + dwSentMode |= smode_add; + + ImmSetConversionStatus( hImc, dwConvMode, dwSentMode ); + + ImmReleaseContext( hwnd, hImc ); + } + } + + bool ConvMatches( DWORD convFlags ) + { + // To match, the active flags have to have none of the remove flags and have to have all of the "add" flags + if ( convFlags & cmode_remove ) + return false; + + if ( ( convFlags & cmode_add ) == cmode_add ) + { + return true; + } + return false; + } + + bool SentMatches( DWORD sentFlags ) + { + // To match, the active flags have to have none of the remove flags and have to have all of the "add" flags + if ( sentFlags & smode_remove ) + return false; + + if ( ( sentFlags & smode_add ) == smode_add ) + { + return true; + } + return false; + } + + unsigned int cmode_remove; + unsigned int cmode_add; + unsigned int smode_remove; + unsigned int smode_add; +}; + +static IMESettingsTransform g_ConversionMode_CHT_ToChinese( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + 0, + 0 ); +static IMESettingsTransform g_ConversionMode_CHT_ToEnglish( + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_CHS_ToChinese( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + 0, + 0 ); +static IMESettingsTransform g_ConversionMode_CHS_ToEnglish( + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_KO_ToKorean( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_KO_ToEnglish( + IME_CMODE_NATIVE | IME_CMODE_LANGUAGE, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_Hiragana( + IME_CMODE_ALPHANUMERIC | IME_CMODE_KATAKANA, + IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_DirectInput( + IME_CMODE_NATIVE | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ) | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN, + IME_CMODE_ALPHANUMERIC, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_FullwidthKatakana( + IME_CMODE_ALPHANUMERIC, + IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN | IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_HalfwidthKatakana( + IME_CMODE_ALPHANUMERIC | IME_CMODE_FULLSHAPE, + IME_CMODE_NATIVE | IME_CMODE_ROMAN | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ), + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_FullwidthAlphanumeric( + IME_CMODE_NATIVE | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ), + IME_CMODE_ALPHANUMERIC | IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN, + 0, + 0 ); + +static IMESettingsTransform g_ConversionMode_JP_HalfwidthAlphanumeric( + IME_CMODE_NATIVE | ( IME_CMODE_KATAKANA | IME_CMODE_LANGUAGE ) | IME_CMODE_FULLSHAPE, + IME_CMODE_ALPHANUMERIC | IME_CMODE_ROMAN, + 0, + 0 ); + +#endif // DO_IME + +int CInputSystem::GetIMEConversionModes( ConversionModeItem *dest, int destcount ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( dest ) + { + memset( dest, 0, destcount * sizeof( ConversionModeItem ) ); + } + + DWORD dwConvMode = 0, dwSentMode = 0; + + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode ); + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } + + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + switch ( info->languageflag ) + { + default: + return 0; + case TRADITIONAL_CHINESE: + // This is either native or alphanumeric + if ( dest ) + { + ConversionModeItem *item; + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Chinese", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHT_ToChinese; + item->active = g_ConversionMode_CHT_ToChinese.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHT_ToEnglish; + item->active = g_ConversionMode_CHT_ToEnglish.ConvMatches( dwConvMode ); + } + return 2; + case JAPANESE: + // There are 6 Japanese modes + if ( dest ) + { + ConversionModeItem *item; + + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Hiragana", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_Hiragana; + item->active = g_ConversionMode_JP_Hiragana.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_FullWidthKatakana", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_FullwidthKatakana; + item->active = g_ConversionMode_JP_FullwidthKatakana.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_FullWidthAlphanumeric", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_FullwidthAlphanumeric; + item->active = g_ConversionMode_JP_FullwidthAlphanumeric.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_HalfWidthKatakana", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_HalfwidthKatakana; + item->active = g_ConversionMode_JP_HalfwidthKatakana.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_HalfWidthAlphanumeric", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_HalfwidthAlphanumeric; + item->active = g_ConversionMode_JP_HalfwidthAlphanumeric.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_JP_DirectInput; + item->active = g_ConversionMode_JP_DirectInput.ConvMatches( dwConvMode ); + + } + return 6; + case KOREAN: + // This is either native or alphanumeric + if ( dest ) + { + ConversionModeItem *item; + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Korean", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_KO_ToKorean; + item->active = g_ConversionMode_KO_ToKorean.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_KO_ToEnglish; + item->active = g_ConversionMode_KO_ToEnglish.ConvMatches( dwConvMode ); + } + return 2; + case SIMPLIFIED_CHINESE: + // This is either native or alphanumeric + if ( dest ) + { + ConversionModeItem *item; + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_Chinese", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHS_ToChinese; + item->active = g_ConversionMode_CHS_ToChinese.ConvMatches( dwConvMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_English", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_ConversionMode_CHS_ToChinese; + item->active = g_ConversionMode_CHS_ToChinese.ConvMatches( dwConvMode ); + } + return 2; + } +#endif + + return 0; +} + +#ifdef DO_IME + +static IMESettingsTransform g_SentenceMode_JP_None( + 0, + 0, + IME_SMODE_PLAURALCLAUSE | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_PHRASEPREDICT | IME_SMODE_CONVERSATION, + IME_SMODE_NONE ); + +static IMESettingsTransform g_SentenceMode_JP_General( + 0, + 0, + IME_SMODE_NONE | IME_SMODE_PLAURALCLAUSE | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_CONVERSATION, + IME_SMODE_PHRASEPREDICT + ); + +static IMESettingsTransform g_SentenceMode_JP_BiasNames( + 0, + 0, + IME_SMODE_NONE | IME_SMODE_PHRASEPREDICT | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_CONVERSATION, + IME_SMODE_PLAURALCLAUSE + ); + +static IMESettingsTransform g_SentenceMode_JP_BiasSpeech( + 0, + 0, + IME_SMODE_NONE | IME_SMODE_PHRASEPREDICT | IME_SMODE_SINGLECONVERT | IME_SMODE_AUTOMATIC | IME_SMODE_PLAURALCLAUSE, + IME_SMODE_CONVERSATION + ); + +#endif // _X360 + +int CInputSystem::GetIMESentenceModes( SentenceModeItem *dest, int destcount ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( dest ) + { + memset( dest, 0, destcount * sizeof( SentenceModeItem ) ); + } + + DWORD dwConvMode = 0, dwSentMode = 0; + + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + ImmGetConversionStatus( hImc, &dwConvMode, &dwSentMode ); + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } + + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + switch ( info->languageflag ) + { + default: + return 0; +// case TRADITIONAL_CHINESE: +// break; + case JAPANESE: + // There are 4 Japanese sentence modes + if ( dest ) + { + SentenceModeItem *item; + + int i = 0; + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_General", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_General; + item->active = g_SentenceMode_JP_General.SentMatches( dwSentMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_BiasNames", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_BiasNames; + item->active = g_SentenceMode_JP_BiasNames.SentMatches( dwSentMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_BiasSpeech", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_BiasSpeech; + item->active = g_SentenceMode_JP_BiasSpeech.SentMatches( dwSentMode ); + + item = &dest[ i++ ]; + wcsncpy( item->menuname, L"#IME_NoConversion", sizeof( item->menuname ) / sizeof( wchar_t ) ); + item->handleValue = (int)&g_SentenceMode_JP_None; + item->active = g_SentenceMode_JP_None.SentMatches( dwSentMode ); + } + return 4; + } +#endif + + return 0; +} + +void CInputSystem::OnChangeIMEConversionModeByHandle( int handleValue ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( handleValue == 0 ) + return; + + IMESettingsTransform *txform = ( IMESettingsTransform * )handleValue; + txform->Apply( (HWND)GetIMEWindow() ); +#endif +} + +void CInputSystem::OnChangeIMESentenceModeByHandle( int handleValue ) +{ +} + +void CInputSystem::OnInputLanguageChanged() +{ +} + +void CInputSystem::OnIMEStartComposition() +{ +} + +#ifdef DO_IME +void DescribeIMEFlag( char const *string, bool value ) +{ + if ( value ) + { + Msg( " %s\n", string ); + } +} + +#define IMEDesc( x ) DescribeIMEFlag( #x, flags & x ); +#endif // DO_IME + +void CInputSystem::OnIMEComposition( int flags ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + /* + Msg( "OnIMEComposition\n" ); + + IMEDesc( VGUI_GCS_COMPREADSTR ); + IMEDesc( VGUI_GCS_COMPREADATTR ); + IMEDesc( VGUI_GCS_COMPREADCLAUSE ); + IMEDesc( VGUI_GCS_COMPSTR ); + IMEDesc( VGUI_GCS_COMPATTR ); + IMEDesc( VGUI_GCS_COMPCLAUSE ); + IMEDesc( VGUI_GCS_CURSORPOS ); + IMEDesc( VGUI_GCS_DELTASTART ); + IMEDesc( VGUI_GCS_RESULTREADSTR ); + IMEDesc( VGUI_GCS_RESULTREADCLAUSE ); + IMEDesc( VGUI_GCS_RESULTSTR ); + IMEDesc( VGUI_GCS_RESULTCLAUSE ); + IMEDesc( VGUI_CS_INSERTCHAR ); + IMEDesc( VGUI_CS_NOMOVECARET ); + */ + + HIMC hIMC = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hIMC ) + { + if ( flags & VGUI_GCS_RESULTSTR ) + { + wchar_t tempstr[ 32 ]; + + int len = ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, (LPVOID)tempstr, sizeof( tempstr ) ); + if ( len > 0 ) + { + if ((len % 2) != 0) + len++; + int numchars = len / sizeof( wchar_t ); + + for ( int i = 0; i < numchars; ++i ) + { + InternalKeyTyped( tempstr[ i ] ); + } + } + } + if ( flags & VGUI_GCS_COMPSTR ) + { + wchar_t tempstr[ 256 ]; + + int len = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, (LPVOID)tempstr, sizeof( tempstr ) ); + if ( len > 0 ) + { + if ((len % 2) != 0) + len++; + int numchars = len / sizeof( wchar_t ); + tempstr[ numchars ] = L'\0'; + + InternalSetCompositionString( tempstr ); + } + } + + ImmReleaseContext( ( HWND )GetIMEWindow(), hIMC ); + } +#endif +} + +void CInputSystem::OnIMEEndComposition() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + // tell the current focused panel that a key was typed + PostKeyMessage( new KeyValues( "DoCompositionString", "string", L"" ) ); + } +} + +void CInputSystem::DestroyCandidateList() +{ +#ifdef DO_IME + if ( _imeCandidates ) + { + delete[] (char *)_imeCandidates; + _imeCandidates = null; + } +#endif +} + +void CInputSystem::OnIMEShowCandidates() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + DestroyCandidateList(); + CreateNewCandidateList(); + + InternalShowCandidateWindow(); +#endif +} + +void CInputSystem::OnIMECloseCandidates() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + InternalHideCandidateWindow(); + DestroyCandidateList(); +#endif +} + +void CInputSystem::OnIMEChangeCandidates() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + DestroyCandidateList(); + CreateNewCandidateList(); + + InternalUpdateCandidateWindow(); +#endif +} + +void CInputSystem::CreateNewCandidateList() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + Assert( !_imeCandidates ); + + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + DWORD numCandidates = 0; + + DWORD bytes = ImmGetCandidateListCountW( hImc, &numCandidates ); + if ( numCandidates > 0 ) + { + DWORD buflen = bytes + 1; + + char *buf = new char[ buflen ]; + Q_memset( buf, 0, buflen ); + + CANDIDATELIST *list = ( CANDIDATELIST *)buf; + DWORD copyBytes = ImmGetCandidateListW( hImc, 0, list, buflen ); + if ( copyBytes > 0 ) + { + _imeCandidates = list; + } + else + { + delete[] buf; + } + } + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } +#endif +} + +int CInputSystem::GetCandidateListCount() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + + return (int)_imeCandidates->dwCount; +#else + return 0; +#endif +} + +void CInputSystem::GetCandidate( int num, wchar_t *dest, int destSizeBytes ) +{ + ASSERT_IF_IME_NYI(); + + dest[ 0 ] = L'\0'; +#ifdef DO_IME + if ( num < 0 || num >= (int)_imeCandidates->dwCount ) + { + return; + } + + DWORD offset = *( DWORD *)( (char *)( _imeCandidates->dwOffset + num ) ); + wchar_t *s = ( wchar_t *)( (char *)_imeCandidates + offset ); + + wcsncpy( dest, s, destSizeBytes / sizeof( wchar_t ) - 1 ); + dest[ destSizeBytes / sizeof( wchar_t ) - 1 ] = L'\0'; +#endif +} + +int CInputSystem::GetCandidateListSelectedItem() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + + return (int)_imeCandidates->dwSelection; +#else + return 0; +#endif +} + +int CInputSystem::GetCandidateListPageSize() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + return (int)_imeCandidates->dwPageSize; +#else + return 0; +#endif +} + +int CInputSystem::GetCandidateListPageStart() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + if ( !_imeCandidates ) + return 0; + return (int)_imeCandidates->dwPageStart; +#else + return 0; +#endif +} + +void CInputSystem::SetCandidateListPageStart( int start ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + HIMC hImc = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hImc ) + { + ImmNotifyIME( hImc, NI_SETCANDIDATE_PAGESTART, 0, start ); + ImmReleaseContext( ( HWND )GetIMEWindow(), hImc ); + } +#endif +} + +void CInputSystem::OnIMERecomputeModes() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CInputSystem::CandidateListStartsAtOne() +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + DWORD prop = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY ); + if ( prop & IME_PROP_CANDLIST_START_FROM_1 ) + { + return true; + } +#endif + return false; +} + +void CInputSystem::SetCandidateWindowPos( int x, int y ) +{ + ASSERT_IF_IME_NYI(); + +#ifdef DO_IME + POINT point; + CANDIDATEFORM Candidate; + + point.x = x; + point.y = y; + + HIMC hIMC = ImmGetContext( ( HWND )GetIMEWindow() ); + if ( hIMC ) + { + // Set candidate window position near caret position + Candidate.dwIndex = 0; + Candidate.dwStyle = CFS_FORCE_POSITION; + Candidate.ptCurrentPos.x = point.x; + Candidate.ptCurrentPos.y = point.y; + ImmSetCandidateWindow( hIMC, &Candidate ); + + ImmReleaseContext( ( HWND )GetIMEWindow(),hIMC ); + } +#endif +} + +void CInputSystem::InternalSetCompositionString( const wchar_t *compstr ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + // tell the current focused panel that a key was typed + PostKeyMessage( new KeyValues( "DoCompositionString", "string", compstr ) ); + } +} + +void CInputSystem::InternalShowCandidateWindow() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + PostKeyMessage( new KeyValues( "DoShowIMECandidates" ) ); + } +} + +void CInputSystem::InternalHideCandidateWindow() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + PostKeyMessage( new KeyValues( "DoHideIMECandidates" ) ); + } +} + +void CInputSystem::InternalUpdateCandidateWindow() +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if ( pContext ) + { + PostKeyMessage( new KeyValues( "DoUpdateIMECandidates" ) ); + } +} + +bool CInputSystem::GetShouldInvertCompositionString() +{ +#ifdef DO_IME + LanguageIds *info = GetLanguageInfo( LOWORD( GetKeyboardLayout( 0 ) ) ); + if ( !info ) + return false; + + // Only Chinese (simplified and traditional) + return info->invertcomposition; +#else + return false; +#endif +} + +void CInputSystem::RegisterKeyCodeUnhandledListener( VPANEL panel ) +{ + if ( !panel ) + return; + + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + VPanel *listener = (VPanel *)panel; + + if ( pContext->m_KeyCodeUnhandledListeners.Find( listener ) == pContext->m_KeyCodeUnhandledListeners.InvalidIndex() ) + { + pContext->m_KeyCodeUnhandledListeners.AddToTail( listener ); + } +} + +void CInputSystem::UnregisterKeyCodeUnhandledListener( VPANEL panel ) +{ + if ( !panel ) + return; + + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + VPanel *listener = (VPanel *)panel; + + pContext->m_KeyCodeUnhandledListeners.FindAndRemove( listener ); +} + + +// Posts unhandled message to all interested panels +void CInputSystem::OnKeyCodeUnhandled( int keyCode ) +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + int c = pContext->m_KeyCodeUnhandledListeners.Count(); + for ( int i = 0; i < c; ++i ) + { + VPanel *listener = pContext->m_KeyCodeUnhandledListeners[ i ]; + g_pIVgui->PostMessage((VPANEL)listener, new KeyValues( "KeyCodeUnhandled", "code", keyCode ), NULL ); + } +} + +void CInputSystem::PostModalSubTreeMessage( VPanel *subTree, bool state ) +{ + InputContext_t *pContext = GetInputContext( m_hContext ); + if( pContext->m_pModalSubTree == NULL ) + return; + + //tell the current focused panel that a key was released + KeyValues *kv = new KeyValues( "ModalSubTree", "state", state ? 1 : 0 ); + g_pIVgui->PostMessage( (VPANEL)pContext->m_pModalSubTree, kv, NULL ); +} + +// Assumes subTree is a child panel of the root panel for the vgui contect +// if restrictMessagesToSubTree is true, then mouse and kb messages are only routed to the subTree and it's children and mouse/kb focus +// can only be on one of the subTree children, if a mouse click occurs outside of the subtree, and "UnhandledMouseClick" message is sent to unhandledMouseClickListener panel +// if it's set +// if restrictMessagesToSubTree is false, then mouse and kb messages are routed as normal except that they are not routed down into the subtree +// however, if a mouse click occurs outside of the subtree, and "UnhandleMouseClick" message is sent to unhandledMouseClickListener panel +// if it's set +void CInputSystem::SetModalSubTree( VPANEL subTree, VPANEL unhandledMouseClickListener, bool restrictMessagesToSubTree /*= true*/ ) +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + if ( pContext->m_pModalSubTree && + pContext->m_pModalSubTree != (VPanel *)subTree ) + { + ReleaseModalSubTree(); + } + + if ( !subTree ) + return; + + pContext->m_pModalSubTree = (VPanel *)subTree; + pContext->m_pUnhandledMouseClickListener = (VPanel *)unhandledMouseClickListener; + pContext->m_bRestrictMessagesToModalSubTree = restrictMessagesToSubTree; + + PostModalSubTreeMessage( pContext->m_pModalSubTree, true ); +} + +void CInputSystem::ReleaseModalSubTree() +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + if ( pContext->m_pModalSubTree ) + { + PostModalSubTreeMessage( pContext->m_pModalSubTree, false ); + } + + pContext->m_pModalSubTree = NULL; + pContext->m_pUnhandledMouseClickListener = NULL; + pContext->m_bRestrictMessagesToModalSubTree = false; + +} + +VPANEL CInputSystem::GetModalSubTree() +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return 0; + + return (VPANEL)pContext->m_pModalSubTree; +} + +// These toggle whether the modal subtree is exclusively receiving messages or conversely whether it's being excluded from receiving messages +void CInputSystem::SetModalSubTreeReceiveMessages( bool state ) +{ + InputContext_t *pContext = GetInputContext(m_hContext); + if ( !pContext ) + return; + + Assert( pContext->m_pModalSubTree ); + if ( !pContext->m_pModalSubTree ) + return; + + pContext->m_bRestrictMessagesToModalSubTree = state; + +} + +bool CInputSystem::ShouldModalSubTreeReceiveMessages() const +{ + InputContext_t *pContext = const_cast< CInputSystem * >( this )->GetInputContext(m_hContext); + if ( !pContext ) + return true; + + return pContext->m_bRestrictMessagesToModalSubTree; +} |