//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Methods associated with the cursor // // $Revision: $ // $NoKeywords: $ //===========================================================================// #if !defined( _X360 ) #define OEMRESOURCE //for OCR_* cursor junk #include "winlite.h" #endif #include #if defined( USE_SDL ) #undef M_PI #include "SDL.h" #endif #include "tier0/dbg.h" #include "tier0/vcrmode.h" #include "tier0/icommandline.h" #include "tier1/utldict.h" #include "Cursor.h" #include "vguimatsurface.h" #include "MatSystemSurface.h" #include "filesystem.h" #if defined( _X360 ) #include "xbox/xbox_win32stubs.h" #endif #if defined( USE_SDL ) #include "materialsystem/imaterialsystem.h" #endif #include "inputsystem/iinputsystem.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace vgui; #if defined( USE_SDL ) static SDL_Cursor *s_pDefaultCursor[ dc_last ]; static SDL_Cursor *s_hCurrentCursor = NULL; static SDL_Cursor *s_hCurrentlySetCursor = NULL; #elif defined( WIN32 ) static HICON s_pDefaultCursor[ dc_last ]; static HICON s_hCurrentCursor = NULL; #endif static bool s_bCursorLocked = false; static bool s_bCursorVisible = true; static int s_nForceCursorVisibleCount = 0; static bool s_bSoftwareCursorActive = false; static int s_nSoftwareCursorTexture = -1; static float s_fSoftwareCursorOffsetX = 0; static float s_fSoftwareCursorOffsetY = 0; static int s_rnSoftwareCursorID[20]; static float s_rfSoftwareCursorOffset[20][2]; static bool s_bSoftwareCursorsInitialized = false; extern CMatSystemSurface g_MatSystemSurface; //----------------------------------------------------------------------------- // Initializes cursors //----------------------------------------------------------------------------- void InitCursors() { // load up all default cursors #if defined( USE_SDL ) s_pDefaultCursor[ dc_none ] = NULL; s_pDefaultCursor[ dc_arrow ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_ARROW ); s_pDefaultCursor[ dc_ibeam ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_IBEAM ); s_pDefaultCursor[ dc_hourglass ]= SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_WAIT ); s_pDefaultCursor[ dc_crosshair ]= SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_CROSSHAIR ); s_pDefaultCursor[ dc_waitarrow ]= SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_WAITARROW ); s_pDefaultCursor[ dc_sizenwse ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZENWSE ); s_pDefaultCursor[ dc_sizenesw ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZENESW ); s_pDefaultCursor[ dc_sizewe ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZEWE ); s_pDefaultCursor[ dc_sizens ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZENS ); s_pDefaultCursor[ dc_sizeall ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZEALL ); s_pDefaultCursor[ dc_no ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_NO ); s_pDefaultCursor[ dc_hand ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_HAND ); s_hCurrentCursor = s_pDefaultCursor[ dc_arrow ]; #elif defined( WIN32 ) s_pDefaultCursor[ dc_none ] = NULL; s_pDefaultCursor[ dc_arrow ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_NORMAL); s_pDefaultCursor[ dc_ibeam ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_IBEAM); s_pDefaultCursor[ dc_hourglass ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_WAIT); s_pDefaultCursor[ dc_crosshair ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_CROSS); s_pDefaultCursor[ dc_waitarrow ] =(HICON)LoadCursor(NULL, (LPCTSTR)32650); s_pDefaultCursor[ dc_up ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_UP); s_pDefaultCursor[ dc_sizenwse ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZENWSE); s_pDefaultCursor[ dc_sizenesw ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZENESW); s_pDefaultCursor[ dc_sizewe ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZEWE); s_pDefaultCursor[ dc_sizens ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZENS); s_pDefaultCursor[ dc_sizeall ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZEALL); s_pDefaultCursor[ dc_no ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_NO); s_pDefaultCursor[ dc_hand ] =(HICON)LoadCursor(NULL, (LPCTSTR)32649); s_hCurrentCursor = s_pDefaultCursor[ dc_arrow ]; #endif s_bCursorLocked = false; s_bCursorVisible = true; s_nForceCursorVisibleCount = 0; } #define USER_CURSOR_MASK 0x80000000 #ifdef WIN32 //----------------------------------------------------------------------------- // Purpose: Simple manager for user loaded windows cursors in vgui //----------------------------------------------------------------------------- class CUserCursorManager { public: void Shutdown(); vgui::HCursor CreateCursorFromFile( char const *curOrAniFile, char const *pPathID ); bool LookupCursor( vgui::HCursor cursor, HCURSOR& handle ); private: CUtlDict< HCURSOR, int > m_UserCursors; }; void CUserCursorManager::Shutdown() { for ( int i = m_UserCursors.First() ; i != m_UserCursors.InvalidIndex(); i = m_UserCursors.Next( i ) ) { ::DestroyCursor( m_UserCursors[ i ] ); } m_UserCursors.RemoveAll(); } vgui::HCursor CUserCursorManager::CreateCursorFromFile( char const *curOrAniFile, char const *pPathID ) { char fn[ 512 ]; Q_strncpy( fn, curOrAniFile, sizeof( fn ) ); Q_strlower( fn ); Q_FixSlashes( fn ); int cursorIndex = m_UserCursors.Find( fn ); if ( cursorIndex != m_UserCursors.InvalidIndex() ) { return cursorIndex | USER_CURSOR_MASK; } g_pFullFileSystem->GetLocalCopy( fn ); char fullpath[ 512 ]; g_pFullFileSystem->RelativePathToFullPath( fn, pPathID, fullpath, sizeof( fullpath ) ); HCURSOR newCursor = (HCURSOR)LoadCursorFromFile( fullpath ); cursorIndex = m_UserCursors.Insert( fn, newCursor ); return cursorIndex | USER_CURSOR_MASK; } bool CUserCursorManager::LookupCursor( vgui::HCursor cursor, HCURSOR& handle ) { if ( !( (int)cursor & USER_CURSOR_MASK ) ) { handle = 0; return false; } int cursorIndex = (int)cursor & ~USER_CURSOR_MASK; if ( !m_UserCursors.IsValidIndex( cursorIndex ) ) { handle = 0; return false; } handle = m_UserCursors[ cursorIndex ]; return true; } static CUserCursorManager g_UserCursors; #endif vgui::HCursor Cursor_CreateCursorFromFile( char const *curOrAniFile, char const *pPathID ) { #ifdef WIN32 return g_UserCursors.CreateCursorFromFile( curOrAniFile, pPathID ); #else return dc_user; #endif } void Cursor_ClearUserCursors() { #ifdef WIN32 g_UserCursors.Shutdown(); #endif } //----------------------------------------------------------------------------- // Initializes all the textures for software cursors //----------------------------------------------------------------------------- int InitSoftwareCursorTexture( const char *pchFilename ) { if( !pchFilename || !*pchFilename ) return -1; int nTextureID = g_MatSystemSurface.DrawGetTextureId( pchFilename ); if( nTextureID == -1 ) { nTextureID = g_MatSystemSurface.CreateNewTextureID(); g_MatSystemSurface.DrawSetTextureFile( nTextureID, pchFilename, true, false ); } return nTextureID; } void InitSoftwareCursors() { if( s_bSoftwareCursorsInitialized ) return; memset( s_rfSoftwareCursorOffset, 0, sizeof( s_rfSoftwareCursorOffset ) ); s_rnSoftwareCursorID[dc_none] = -1; s_rnSoftwareCursorID[dc_arrow] =InitSoftwareCursorTexture( "vgui/cursors/arrow" ); s_rnSoftwareCursorID[dc_ibeam] =InitSoftwareCursorTexture( "vgui/cursors/ibeam" ); s_rnSoftwareCursorID[dc_hourglass]=InitSoftwareCursorTexture( "vgui/cursors/hourglass" ); s_rnSoftwareCursorID[dc_crosshair]=InitSoftwareCursorTexture( "vgui/cursors/crosshair" ); s_rnSoftwareCursorID[dc_waitarrow]=InitSoftwareCursorTexture( "vgui/cursors/waitarrow" ); s_rnSoftwareCursorID[dc_up] =InitSoftwareCursorTexture( "vgui/cursors/up" ); s_rnSoftwareCursorID[dc_sizenwse] =InitSoftwareCursorTexture( "vgui/cursors/sizenwse" ); s_rnSoftwareCursorID[dc_sizenesw] =InitSoftwareCursorTexture( "vgui/cursors/sizenesw" ); s_rnSoftwareCursorID[dc_sizewe] =InitSoftwareCursorTexture( "vgui/cursors/sizewe" ); s_rnSoftwareCursorID[dc_sizens] =InitSoftwareCursorTexture( "vgui/cursors/sizens" ); s_rnSoftwareCursorID[dc_sizeall] =InitSoftwareCursorTexture( "vgui/cursors/sizeall" ); s_rnSoftwareCursorID[dc_no] =InitSoftwareCursorTexture( "vgui/cursors/no" ); s_rnSoftwareCursorID[dc_hand] =InitSoftwareCursorTexture( "vgui/cursors/hand" ); // handle the cursor hotspots not being at their origin s_rfSoftwareCursorOffset[dc_arrow][0] = -0.1; s_rfSoftwareCursorOffset[dc_arrow][1] = -0.1; s_rfSoftwareCursorOffset[dc_ibeam][0] = -0.5; s_rfSoftwareCursorOffset[dc_ibeam][1] = -0.8; s_rfSoftwareCursorOffset[dc_hourglass][0] = -0.5; s_rfSoftwareCursorOffset[dc_hourglass][1] = -0.5; s_rfSoftwareCursorOffset[dc_crosshair][0] = -0.5; s_rfSoftwareCursorOffset[dc_crosshair][1] = -0.5; s_rfSoftwareCursorOffset[dc_waitarrow][0] = -0.1; s_rfSoftwareCursorOffset[dc_waitarrow][1] = -0.1; s_rfSoftwareCursorOffset[dc_up][0] = -0.5; s_rfSoftwareCursorOffset[dc_up][1] = -0.5; s_rfSoftwareCursorOffset[dc_sizenwse][0] = -0.5; s_rfSoftwareCursorOffset[dc_sizenwse][1] = -0.5; s_rfSoftwareCursorOffset[dc_sizenesw][0] = -0.5; s_rfSoftwareCursorOffset[dc_sizenesw][1] = -0.5; s_rfSoftwareCursorOffset[dc_sizewe][0] = -0.5; s_rfSoftwareCursorOffset[dc_sizewe][1] = -0.5; s_rfSoftwareCursorOffset[dc_sizens][0] = -0.5; s_rfSoftwareCursorOffset[dc_sizens][1] = -0.5; s_rfSoftwareCursorOffset[dc_sizeall][0] = -0.5; s_rfSoftwareCursorOffset[dc_sizeall][1] = -0.5; s_rfSoftwareCursorOffset[dc_no][0] = -0.5; s_rfSoftwareCursorOffset[dc_no][1] = -0.5; s_rfSoftwareCursorOffset[dc_hand][0] = -0.5; s_rfSoftwareCursorOffset[dc_hand][1] = -0.5; s_bSoftwareCursorsInitialized = true; } //----------------------------------------------------------------------------- // Selects a cursor //----------------------------------------------------------------------------- void CursorSelect(HCursor hCursor) { if ( ( hCursor == dc_alwaysvisible_push ) || ( hCursor == dc_alwaysvisible_pop ) ) { // CConPanel in engine/console.cpp does a SetCursor(null). So when the TF2 chat window pops up // and there are console commands showing and fading out in the top left, our chat window // will have a cursor show/hide fight with them. So the cursor flickers or doesn't show up // at all. Unfortunately on Linux, it's even worse since we recenter the mouse when it's // not shown - so we added this API call which causes cursor.cpp to always show the cursor. s_nForceCursorVisibleCount += ( hCursor == dc_alwaysvisible_push ? 1 : -1 ); Assert( s_nForceCursorVisibleCount >= 0 ); if( ( s_nForceCursorVisibleCount && !s_bCursorVisible ) || ( !s_nForceCursorVisibleCount && s_bCursorVisible ) ) { ActivateCurrentCursor(); } return; } if (s_bCursorLocked) return; #if defined( WIN32 ) && !defined( USE_SDL ) s_bCursorVisible = true; switch (hCursor) { case dc_user: case dc_none: case dc_blank: s_bCursorVisible = false; break; case dc_arrow: case dc_waitarrow: case dc_ibeam: case dc_hourglass: case dc_crosshair: case dc_up: case dc_sizenwse: case dc_sizenesw: case dc_sizewe: case dc_sizens: case dc_sizeall: case dc_no: case dc_hand: if( !s_bSoftwareCursorActive ) { s_hCurrentCursor = s_pDefaultCursor[hCursor]; } else { s_nSoftwareCursorTexture = s_rnSoftwareCursorID[ hCursor ]; s_fSoftwareCursorOffsetX = s_rfSoftwareCursorOffset[ hCursor ][0]; s_fSoftwareCursorOffsetY = s_rfSoftwareCursorOffset[ hCursor ][1]; } break; default: { HCURSOR custom = 0; if ( g_UserCursors.LookupCursor( hCursor, custom ) && custom != 0 ) { s_hCurrentCursor = custom; } else { s_bCursorVisible = false; Assert(0); } } break; } ActivateCurrentCursor(); #elif defined( USE_SDL ) switch (hCursor) { case dc_user: case dc_none: case dc_blank: s_bCursorVisible = false; break; default: // We don't support custom cursors at the moment (but could, if necessary). // Fall through and use the arrow for now... Assert(0); hCursor = dc_arrow; case dc_arrow: case dc_waitarrow: case dc_ibeam: case dc_hourglass: case dc_crosshair: case dc_up: case dc_sizenwse: case dc_sizenesw: case dc_sizewe: case dc_sizens: case dc_sizeall: case dc_no: case dc_hand: s_bCursorVisible = true; if( !s_bSoftwareCursorActive ) { s_hCurrentCursor = s_pDefaultCursor[hCursor]; } else { s_nSoftwareCursorTexture = s_rnSoftwareCursorID[ hCursor ]; s_fSoftwareCursorOffsetX = s_rfSoftwareCursorOffset[ hCursor ][0]; s_fSoftwareCursorOffsetY = s_rfSoftwareCursorOffset[ hCursor ][1]; } break; } ActivateCurrentCursor(); #else #error #endif } //----------------------------------------------------------------------------- // Hides the hardware cursor //----------------------------------------------------------------------------- void HideHardwareCursor() { #if defined( WIN32 ) && !defined( USE_SDL ) ::SetCursor(NULL); #elif defined( USE_SDL ) //if ( s_hCurrentlySetCursor != s_pDefaultCursor[ dc_none ] ) { s_hCurrentlySetCursor = s_pDefaultCursor[ dc_none ]; g_pLauncherMgr->SetMouseCursor( s_hCurrentlySetCursor ); g_pLauncherMgr->SetMouseVisible( false ); } #else #error #endif } //----------------------------------------------------------------------------- // Activates the current cursor //----------------------------------------------------------------------------- void ActivateCurrentCursor() { if( s_bSoftwareCursorActive ) { HideHardwareCursor(); return; } if ( s_bCursorVisible || ( s_nForceCursorVisibleCount > 0 ) ) { #if defined( WIN32 ) && !defined( USE_SDL ) ::SetCursor(s_hCurrentCursor); #elif defined( USE_SDL ) if (s_hCurrentlySetCursor != s_hCurrentCursor ) { s_hCurrentlySetCursor = s_hCurrentCursor; g_pLauncherMgr->SetMouseCursor( s_hCurrentlySetCursor ); g_pLauncherMgr->SetMouseVisible( true ); } #else #error #endif } else { HideHardwareCursor(); } } //----------------------------------------------------------------------------- // Purpose: prevents vgui from changing the cursor //----------------------------------------------------------------------------- void LockCursor( bool bEnable ) { s_bCursorLocked = bEnable; ActivateCurrentCursor(); } //----------------------------------------------------------------------------- // Purpose: unlocks the cursor state //----------------------------------------------------------------------------- bool IsCursorLocked() { return s_bCursorLocked; } //----------------------------------------------------------------------------- // handles mouse movement //----------------------------------------------------------------------------- void CursorSetPos( void *hwnd, int x, int y ) { #if defined( USE_SDL ) if ( s_bCursorVisible ) #endif g_pInputSystem->SetCursorPosition( x, y ); } void CursorGetPos(void *hwnd, int &x, int &y) { #if defined ( USE_SDL ) && !defined( PLATFORM_WINDOWS ) if ( s_bCursorVisible ) { SDL_GetMouseState( &x, &y ); int windowHeight = 0; int windowWidth = 0; //unsigned int ignored; SDL_GetWindowSize( ( SDL_Window * )g_pLauncherMgr->GetWindowRef(), &windowWidth, &windowHeight ); CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); int rx, ry, width, height; pRenderContext->GetViewport( rx, ry, width, height ); if ( !s_bSoftwareCursorActive && (width != windowWidth || height != windowHeight ) ) { // scale the x/y back into the co-ords of the back buffer, not the scaled up window //DevMsg( "Mouse x:%d y:%d %d %d %d %d\n", x, y, width, windowWidth, height, abs( height - windowHeight ) ); x = x * (float)width/windowWidth; y = y * (float)height/windowHeight; } } else { // cursor is invisible, just say we have it pinned to the middle of the screen CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); int rx, ry, width, height; pRenderContext->GetViewport( rx, ry, width, height ); x = rx + width/2; y = ry + height/2; //printf( "Mouse(inv) x:%d y:%d %d %d\n", x, y, width, height ); } #else POINT pt; // Default implementation VCRHook_GetCursorPos( &pt ); VCRHook_ScreenToClient((HWND)hwnd, &pt); x = pt.x; y = pt.y; #endif } void EnableSoftwareCursor( bool bEnable ) { if( bEnable ) InitSoftwareCursors(); bool bWasEnabled = s_bSoftwareCursorActive; s_bSoftwareCursorActive = bEnable; // set the cursor to the arrow (or none if appropriate) if we're activating the // software cursor. VGUI will likely update it again soon, but this will give // us some kind of cursor in the meantime if( !bWasEnabled && bEnable ) { if( s_bCursorVisible ) CursorSelect( dc_arrow ); } } bool ShouldDrawSoftwareCursor() { return s_bSoftwareCursorActive && s_bCursorVisible; } int GetSoftwareCursorTexture( float *px, float *py ) { if( px && py ) { *px = s_fSoftwareCursorOffsetX; *py = s_fSoftwareCursorOffsetY; } return s_nSoftwareCursorTexture; }