Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

565 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Methods associated with the cursor
  4. //
  5. // $Revision: $
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #if !defined( _X360 )
  9. #define OEMRESOURCE //for OCR_* cursor junk
  10. #include "winlite.h"
  11. #endif
  12. #include <appframework/ilaunchermgr.h>
  13. #if defined( USE_SDL )
  14. #undef M_PI
  15. #include "SDL.h"
  16. #endif
  17. #include "tier0/dbg.h"
  18. #include "tier0/vcrmode.h"
  19. #include "tier0/icommandline.h"
  20. #include "tier1/utldict.h"
  21. #include "Cursor.h"
  22. #include "vguimatsurface.h"
  23. #include "MatSystemSurface.h"
  24. #include "filesystem.h"
  25. #if defined( _X360 )
  26. #include "xbox/xbox_win32stubs.h"
  27. #endif
  28. #if defined( USE_SDL )
  29. #include "materialsystem/imaterialsystem.h"
  30. #endif
  31. #include "inputsystem/iinputsystem.h"
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. using namespace vgui;
  35. #if defined( USE_SDL )
  36. static SDL_Cursor *s_pDefaultCursor[ dc_last ];
  37. static SDL_Cursor *s_hCurrentCursor = NULL;
  38. static SDL_Cursor *s_hCurrentlySetCursor = NULL;
  39. #elif defined( WIN32 )
  40. static HICON s_pDefaultCursor[ dc_last ];
  41. static HICON s_hCurrentCursor = NULL;
  42. #endif
  43. static bool s_bCursorLocked = false;
  44. static bool s_bCursorVisible = true;
  45. static int s_nForceCursorVisibleCount = 0;
  46. static bool s_bSoftwareCursorActive = false;
  47. static int s_nSoftwareCursorTexture = -1;
  48. static float s_fSoftwareCursorOffsetX = 0;
  49. static float s_fSoftwareCursorOffsetY = 0;
  50. static int s_rnSoftwareCursorID[20];
  51. static float s_rfSoftwareCursorOffset[20][2];
  52. static bool s_bSoftwareCursorsInitialized = false;
  53. extern CMatSystemSurface g_MatSystemSurface;
  54. //-----------------------------------------------------------------------------
  55. // Initializes cursors
  56. //-----------------------------------------------------------------------------
  57. void InitCursors()
  58. {
  59. // load up all default cursors
  60. #if defined( USE_SDL )
  61. s_pDefaultCursor[ dc_none ] = NULL;
  62. s_pDefaultCursor[ dc_arrow ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_ARROW );
  63. s_pDefaultCursor[ dc_ibeam ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_IBEAM );
  64. s_pDefaultCursor[ dc_hourglass ]= SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_WAIT );
  65. s_pDefaultCursor[ dc_crosshair ]= SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_CROSSHAIR );
  66. s_pDefaultCursor[ dc_waitarrow ]= SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_WAITARROW );
  67. s_pDefaultCursor[ dc_sizenwse ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZENWSE );
  68. s_pDefaultCursor[ dc_sizenesw ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZENESW );
  69. s_pDefaultCursor[ dc_sizewe ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZEWE );
  70. s_pDefaultCursor[ dc_sizens ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZENS );
  71. s_pDefaultCursor[ dc_sizeall ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_SIZEALL );
  72. s_pDefaultCursor[ dc_no ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_NO );
  73. s_pDefaultCursor[ dc_hand ] = SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_HAND );
  74. s_hCurrentCursor = s_pDefaultCursor[ dc_arrow ];
  75. #elif defined( WIN32 )
  76. s_pDefaultCursor[ dc_none ] = NULL;
  77. s_pDefaultCursor[ dc_arrow ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_NORMAL);
  78. s_pDefaultCursor[ dc_ibeam ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_IBEAM);
  79. s_pDefaultCursor[ dc_hourglass ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_WAIT);
  80. s_pDefaultCursor[ dc_crosshair ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_CROSS);
  81. s_pDefaultCursor[ dc_waitarrow ] =(HICON)LoadCursor(NULL, (LPCTSTR)32650);
  82. s_pDefaultCursor[ dc_up ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_UP);
  83. s_pDefaultCursor[ dc_sizenwse ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZENWSE);
  84. s_pDefaultCursor[ dc_sizenesw ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZENESW);
  85. s_pDefaultCursor[ dc_sizewe ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZEWE);
  86. s_pDefaultCursor[ dc_sizens ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZENS);
  87. s_pDefaultCursor[ dc_sizeall ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_SIZEALL);
  88. s_pDefaultCursor[ dc_no ] =(HICON)LoadCursor(NULL, (LPCTSTR)OCR_NO);
  89. s_pDefaultCursor[ dc_hand ] =(HICON)LoadCursor(NULL, (LPCTSTR)32649);
  90. s_hCurrentCursor = s_pDefaultCursor[ dc_arrow ];
  91. #endif
  92. s_bCursorLocked = false;
  93. s_bCursorVisible = true;
  94. s_nForceCursorVisibleCount = 0;
  95. }
  96. #define USER_CURSOR_MASK 0x80000000
  97. #ifdef WIN32
  98. //-----------------------------------------------------------------------------
  99. // Purpose: Simple manager for user loaded windows cursors in vgui
  100. //-----------------------------------------------------------------------------
  101. class CUserCursorManager
  102. {
  103. public:
  104. void Shutdown();
  105. vgui::HCursor CreateCursorFromFile( char const *curOrAniFile, char const *pPathID );
  106. bool LookupCursor( vgui::HCursor cursor, HCURSOR& handle );
  107. private:
  108. CUtlDict< HCURSOR, int > m_UserCursors;
  109. };
  110. void CUserCursorManager::Shutdown()
  111. {
  112. for ( int i = m_UserCursors.First() ; i != m_UserCursors.InvalidIndex(); i = m_UserCursors.Next( i ) )
  113. {
  114. ::DestroyCursor( m_UserCursors[ i ] );
  115. }
  116. m_UserCursors.RemoveAll();
  117. }
  118. vgui::HCursor CUserCursorManager::CreateCursorFromFile( char const *curOrAniFile, char const *pPathID )
  119. {
  120. char fn[ 512 ];
  121. Q_strncpy( fn, curOrAniFile, sizeof( fn ) );
  122. Q_strlower( fn );
  123. Q_FixSlashes( fn );
  124. int cursorIndex = m_UserCursors.Find( fn );
  125. if ( cursorIndex != m_UserCursors.InvalidIndex() )
  126. {
  127. return cursorIndex | USER_CURSOR_MASK;
  128. }
  129. g_pFullFileSystem->GetLocalCopy( fn );
  130. char fullpath[ 512 ];
  131. g_pFullFileSystem->RelativePathToFullPath( fn, pPathID, fullpath, sizeof( fullpath ) );
  132. HCURSOR newCursor = (HCURSOR)LoadCursorFromFile( fullpath );
  133. cursorIndex = m_UserCursors.Insert( fn, newCursor );
  134. return cursorIndex | USER_CURSOR_MASK;
  135. }
  136. bool CUserCursorManager::LookupCursor( vgui::HCursor cursor, HCURSOR& handle )
  137. {
  138. if ( !( (int)cursor & USER_CURSOR_MASK ) )
  139. {
  140. handle = 0;
  141. return false;
  142. }
  143. int cursorIndex = (int)cursor & ~USER_CURSOR_MASK;
  144. if ( !m_UserCursors.IsValidIndex( cursorIndex ) )
  145. {
  146. handle = 0;
  147. return false;
  148. }
  149. handle = m_UserCursors[ cursorIndex ];
  150. return true;
  151. }
  152. static CUserCursorManager g_UserCursors;
  153. #endif
  154. vgui::HCursor Cursor_CreateCursorFromFile( char const *curOrAniFile, char const *pPathID )
  155. {
  156. #ifdef WIN32
  157. return g_UserCursors.CreateCursorFromFile( curOrAniFile, pPathID );
  158. #else
  159. return dc_user;
  160. #endif
  161. }
  162. void Cursor_ClearUserCursors()
  163. {
  164. #ifdef WIN32
  165. g_UserCursors.Shutdown();
  166. #endif
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Initializes all the textures for software cursors
  170. //-----------------------------------------------------------------------------
  171. int InitSoftwareCursorTexture( const char *pchFilename )
  172. {
  173. if( !pchFilename || !*pchFilename )
  174. return -1;
  175. int nTextureID = g_MatSystemSurface.DrawGetTextureId( pchFilename );
  176. if( nTextureID == -1 )
  177. {
  178. nTextureID = g_MatSystemSurface.CreateNewTextureID();
  179. g_MatSystemSurface.DrawSetTextureFile( nTextureID, pchFilename, true, false );
  180. }
  181. return nTextureID;
  182. }
  183. void InitSoftwareCursors()
  184. {
  185. if( s_bSoftwareCursorsInitialized )
  186. return;
  187. memset( s_rfSoftwareCursorOffset, 0, sizeof( s_rfSoftwareCursorOffset ) );
  188. s_rnSoftwareCursorID[dc_none] = -1;
  189. s_rnSoftwareCursorID[dc_arrow] =InitSoftwareCursorTexture( "vgui/cursors/arrow" );
  190. s_rnSoftwareCursorID[dc_ibeam] =InitSoftwareCursorTexture( "vgui/cursors/ibeam" );
  191. s_rnSoftwareCursorID[dc_hourglass]=InitSoftwareCursorTexture( "vgui/cursors/hourglass" );
  192. s_rnSoftwareCursorID[dc_crosshair]=InitSoftwareCursorTexture( "vgui/cursors/crosshair" );
  193. s_rnSoftwareCursorID[dc_waitarrow]=InitSoftwareCursorTexture( "vgui/cursors/waitarrow" );
  194. s_rnSoftwareCursorID[dc_up] =InitSoftwareCursorTexture( "vgui/cursors/up" );
  195. s_rnSoftwareCursorID[dc_sizenwse] =InitSoftwareCursorTexture( "vgui/cursors/sizenwse" );
  196. s_rnSoftwareCursorID[dc_sizenesw] =InitSoftwareCursorTexture( "vgui/cursors/sizenesw" );
  197. s_rnSoftwareCursorID[dc_sizewe] =InitSoftwareCursorTexture( "vgui/cursors/sizewe" );
  198. s_rnSoftwareCursorID[dc_sizens] =InitSoftwareCursorTexture( "vgui/cursors/sizens" );
  199. s_rnSoftwareCursorID[dc_sizeall] =InitSoftwareCursorTexture( "vgui/cursors/sizeall" );
  200. s_rnSoftwareCursorID[dc_no] =InitSoftwareCursorTexture( "vgui/cursors/no" );
  201. s_rnSoftwareCursorID[dc_hand] =InitSoftwareCursorTexture( "vgui/cursors/hand" );
  202. // handle the cursor hotspots not being at their origin
  203. s_rfSoftwareCursorOffset[dc_arrow][0] = -0.1;
  204. s_rfSoftwareCursorOffset[dc_arrow][1] = -0.1;
  205. s_rfSoftwareCursorOffset[dc_ibeam][0] = -0.5;
  206. s_rfSoftwareCursorOffset[dc_ibeam][1] = -0.8;
  207. s_rfSoftwareCursorOffset[dc_hourglass][0] = -0.5;
  208. s_rfSoftwareCursorOffset[dc_hourglass][1] = -0.5;
  209. s_rfSoftwareCursorOffset[dc_crosshair][0] = -0.5;
  210. s_rfSoftwareCursorOffset[dc_crosshair][1] = -0.5;
  211. s_rfSoftwareCursorOffset[dc_waitarrow][0] = -0.1;
  212. s_rfSoftwareCursorOffset[dc_waitarrow][1] = -0.1;
  213. s_rfSoftwareCursorOffset[dc_up][0] = -0.5;
  214. s_rfSoftwareCursorOffset[dc_up][1] = -0.5;
  215. s_rfSoftwareCursorOffset[dc_sizenwse][0] = -0.5;
  216. s_rfSoftwareCursorOffset[dc_sizenwse][1] = -0.5;
  217. s_rfSoftwareCursorOffset[dc_sizenesw][0] = -0.5;
  218. s_rfSoftwareCursorOffset[dc_sizenesw][1] = -0.5;
  219. s_rfSoftwareCursorOffset[dc_sizewe][0] = -0.5;
  220. s_rfSoftwareCursorOffset[dc_sizewe][1] = -0.5;
  221. s_rfSoftwareCursorOffset[dc_sizens][0] = -0.5;
  222. s_rfSoftwareCursorOffset[dc_sizens][1] = -0.5;
  223. s_rfSoftwareCursorOffset[dc_sizeall][0] = -0.5;
  224. s_rfSoftwareCursorOffset[dc_sizeall][1] = -0.5;
  225. s_rfSoftwareCursorOffset[dc_no][0] = -0.5;
  226. s_rfSoftwareCursorOffset[dc_no][1] = -0.5;
  227. s_rfSoftwareCursorOffset[dc_hand][0] = -0.5;
  228. s_rfSoftwareCursorOffset[dc_hand][1] = -0.5;
  229. s_bSoftwareCursorsInitialized = true;
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Selects a cursor
  233. //-----------------------------------------------------------------------------
  234. void CursorSelect(HCursor hCursor)
  235. {
  236. if ( ( hCursor == dc_alwaysvisible_push ) || ( hCursor == dc_alwaysvisible_pop ) )
  237. {
  238. // CConPanel in engine/console.cpp does a SetCursor(null). So when the TF2 chat window pops up
  239. // and there are console commands showing and fading out in the top left, our chat window
  240. // will have a cursor show/hide fight with them. So the cursor flickers or doesn't show up
  241. // at all. Unfortunately on Linux, it's even worse since we recenter the mouse when it's
  242. // not shown - so we added this API call which causes cursor.cpp to always show the cursor.
  243. s_nForceCursorVisibleCount += ( hCursor == dc_alwaysvisible_push ? 1 : -1 );
  244. Assert( s_nForceCursorVisibleCount >= 0 );
  245. if( ( s_nForceCursorVisibleCount && !s_bCursorVisible ) ||
  246. ( !s_nForceCursorVisibleCount && s_bCursorVisible ) )
  247. {
  248. ActivateCurrentCursor();
  249. }
  250. return;
  251. }
  252. if (s_bCursorLocked)
  253. return;
  254. #if defined( WIN32 ) && !defined( USE_SDL )
  255. s_bCursorVisible = true;
  256. switch (hCursor)
  257. {
  258. case dc_user:
  259. case dc_none:
  260. case dc_blank:
  261. s_bCursorVisible = false;
  262. break;
  263. case dc_arrow:
  264. case dc_waitarrow:
  265. case dc_ibeam:
  266. case dc_hourglass:
  267. case dc_crosshair:
  268. case dc_up:
  269. case dc_sizenwse:
  270. case dc_sizenesw:
  271. case dc_sizewe:
  272. case dc_sizens:
  273. case dc_sizeall:
  274. case dc_no:
  275. case dc_hand:
  276. if( !s_bSoftwareCursorActive )
  277. {
  278. s_hCurrentCursor = s_pDefaultCursor[hCursor];
  279. }
  280. else
  281. {
  282. s_nSoftwareCursorTexture = s_rnSoftwareCursorID[ hCursor ];
  283. s_fSoftwareCursorOffsetX = s_rfSoftwareCursorOffset[ hCursor ][0];
  284. s_fSoftwareCursorOffsetY = s_rfSoftwareCursorOffset[ hCursor ][1];
  285. }
  286. break;
  287. default:
  288. {
  289. HCURSOR custom = 0;
  290. if ( g_UserCursors.LookupCursor( hCursor, custom ) && custom != 0 )
  291. {
  292. s_hCurrentCursor = custom;
  293. }
  294. else
  295. {
  296. s_bCursorVisible = false;
  297. Assert(0);
  298. }
  299. }
  300. break;
  301. }
  302. ActivateCurrentCursor();
  303. #elif defined( USE_SDL )
  304. switch (hCursor)
  305. {
  306. case dc_user:
  307. case dc_none:
  308. case dc_blank:
  309. s_bCursorVisible = false;
  310. break;
  311. default:
  312. // We don't support custom cursors at the moment (but could, if necessary).
  313. // Fall through and use the arrow for now...
  314. Assert(0);
  315. hCursor = dc_arrow;
  316. case dc_arrow:
  317. case dc_waitarrow:
  318. case dc_ibeam:
  319. case dc_hourglass:
  320. case dc_crosshair:
  321. case dc_up:
  322. case dc_sizenwse:
  323. case dc_sizenesw:
  324. case dc_sizewe:
  325. case dc_sizens:
  326. case dc_sizeall:
  327. case dc_no:
  328. case dc_hand:
  329. s_bCursorVisible = true;
  330. if( !s_bSoftwareCursorActive )
  331. {
  332. s_hCurrentCursor = s_pDefaultCursor[hCursor];
  333. }
  334. else
  335. {
  336. s_nSoftwareCursorTexture = s_rnSoftwareCursorID[ hCursor ];
  337. s_fSoftwareCursorOffsetX = s_rfSoftwareCursorOffset[ hCursor ][0];
  338. s_fSoftwareCursorOffsetY = s_rfSoftwareCursorOffset[ hCursor ][1];
  339. }
  340. break;
  341. }
  342. ActivateCurrentCursor();
  343. #else
  344. #error
  345. #endif
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Hides the hardware cursor
  349. //-----------------------------------------------------------------------------
  350. void HideHardwareCursor()
  351. {
  352. #if defined( WIN32 ) && !defined( USE_SDL )
  353. ::SetCursor(NULL);
  354. #elif defined( USE_SDL )
  355. //if ( s_hCurrentlySetCursor != s_pDefaultCursor[ dc_none ] )
  356. {
  357. s_hCurrentlySetCursor = s_pDefaultCursor[ dc_none ];
  358. g_pLauncherMgr->SetMouseCursor( s_hCurrentlySetCursor );
  359. g_pLauncherMgr->SetMouseVisible( false );
  360. }
  361. #else
  362. #error
  363. #endif
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Activates the current cursor
  367. //-----------------------------------------------------------------------------
  368. void ActivateCurrentCursor()
  369. {
  370. if( s_bSoftwareCursorActive )
  371. {
  372. HideHardwareCursor();
  373. return;
  374. }
  375. if ( s_bCursorVisible || ( s_nForceCursorVisibleCount > 0 ) )
  376. {
  377. #if defined( WIN32 ) && !defined( USE_SDL )
  378. ::SetCursor(s_hCurrentCursor);
  379. #elif defined( USE_SDL )
  380. if (s_hCurrentlySetCursor != s_hCurrentCursor )
  381. {
  382. s_hCurrentlySetCursor = s_hCurrentCursor;
  383. g_pLauncherMgr->SetMouseCursor( s_hCurrentlySetCursor );
  384. g_pLauncherMgr->SetMouseVisible( true );
  385. }
  386. #else
  387. #error
  388. #endif
  389. }
  390. else
  391. {
  392. HideHardwareCursor();
  393. }
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose: prevents vgui from changing the cursor
  397. //-----------------------------------------------------------------------------
  398. void LockCursor( bool bEnable )
  399. {
  400. s_bCursorLocked = bEnable;
  401. ActivateCurrentCursor();
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: unlocks the cursor state
  405. //-----------------------------------------------------------------------------
  406. bool IsCursorLocked()
  407. {
  408. return s_bCursorLocked;
  409. }
  410. //-----------------------------------------------------------------------------
  411. // handles mouse movement
  412. //-----------------------------------------------------------------------------
  413. void CursorSetPos( void *hwnd, int x, int y )
  414. {
  415. #if defined( USE_SDL )
  416. if ( s_bCursorVisible )
  417. #endif
  418. g_pInputSystem->SetCursorPosition( x, y );
  419. }
  420. void CursorGetPos(void *hwnd, int &x, int &y)
  421. {
  422. #if defined ( USE_SDL ) && !defined( PLATFORM_WINDOWS )
  423. if ( s_bCursorVisible )
  424. {
  425. SDL_GetMouseState( &x, &y );
  426. int windowHeight = 0;
  427. int windowWidth = 0;
  428. //unsigned int ignored;
  429. SDL_GetWindowSize( ( SDL_Window * )g_pLauncherMgr->GetWindowRef(), &windowWidth, &windowHeight );
  430. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  431. int rx, ry, width, height;
  432. pRenderContext->GetViewport( rx, ry, width, height );
  433. if ( !s_bSoftwareCursorActive && (width != windowWidth || height != windowHeight ) )
  434. {
  435. // scale the x/y back into the co-ords of the back buffer, not the scaled up window
  436. //DevMsg( "Mouse x:%d y:%d %d %d %d %d\n", x, y, width, windowWidth, height, abs( height - windowHeight ) );
  437. x = x * (float)width/windowWidth;
  438. y = y * (float)height/windowHeight;
  439. }
  440. }
  441. else
  442. {
  443. // cursor is invisible, just say we have it pinned to the middle of the screen
  444. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  445. int rx, ry, width, height;
  446. pRenderContext->GetViewport( rx, ry, width, height );
  447. x = rx + width/2;
  448. y = ry + height/2;
  449. //printf( "Mouse(inv) x:%d y:%d %d %d\n", x, y, width, height );
  450. }
  451. #else
  452. POINT pt;
  453. // Default implementation
  454. VCRHook_GetCursorPos( &pt );
  455. VCRHook_ScreenToClient((HWND)hwnd, &pt);
  456. x = pt.x; y = pt.y;
  457. #endif
  458. }
  459. void EnableSoftwareCursor( bool bEnable )
  460. {
  461. if( bEnable )
  462. InitSoftwareCursors();
  463. bool bWasEnabled = s_bSoftwareCursorActive;
  464. s_bSoftwareCursorActive = bEnable;
  465. // set the cursor to the arrow (or none if appropriate) if we're activating the
  466. // software cursor. VGUI will likely update it again soon, but this will give
  467. // us some kind of cursor in the meantime
  468. if( !bWasEnabled && bEnable )
  469. {
  470. if( s_bCursorVisible )
  471. CursorSelect( dc_arrow );
  472. }
  473. }
  474. bool ShouldDrawSoftwareCursor()
  475. {
  476. return s_bSoftwareCursorActive && s_bCursorVisible;
  477. }
  478. int GetSoftwareCursorTexture( float *px, float *py )
  479. {
  480. if( px && py )
  481. {
  482. *px = s_fSoftwareCursorOffsetX;
  483. *py = s_fSoftwareCursorOffsetY;
  484. }
  485. return s_nSoftwareCursorTexture;
  486. }