MOUSE.CPP 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /*
  2. ** Command & Conquer Red Alert(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. * *
  20. * Project Name : Westwood 32 bit Library *
  21. * *
  22. * File Name : MOUSE.CPP *
  23. * *
  24. * Programmer : Philip W. Gorrow *
  25. * *
  26. * Start Date : 12/12/95 *
  27. * *
  28. * Last Update : December 12, 1995 [PWG] *
  29. * *
  30. *---------------------------------------------------------------------------------------------*
  31. * Functions: *
  32. * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class *
  33. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  34. #include "mouse.h"
  35. #include <mmsystem.h>
  36. static WWMouseClass *_Mouse=NULL;
  37. void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 );
  38. extern BOOL GameInFocus;
  39. /***********************************************************************************************
  40. * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class *
  41. * *
  42. * INPUT: GraphicViewPortClass * screen - pointer to screen mouse is created for *
  43. * *
  44. * OUTPUT: none *
  45. * *
  46. * HISTORY: *
  47. * 12/12/1995 PWG : Created. *
  48. *=============================================================================================*/
  49. WWMouseClass::WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height)
  50. {
  51. MouseCursor = new char[mouse_max_width * mouse_max_height];
  52. MouseXHot = 0;
  53. MouseYHot = 0;
  54. CursorWidth = 0;
  55. CursorHeight = 0;
  56. MouseBuffer = new char[mouse_max_width * mouse_max_height];
  57. MouseBuffX = -1;
  58. MouseBuffY = -1;
  59. MaxWidth = mouse_max_width;
  60. MaxHeight = mouse_max_height;
  61. MouseCXLeft = 0;
  62. MouseCYUpper = 0;
  63. MouseCXRight = 0;
  64. MouseCYLower = 0;
  65. MCFlags = 0;
  66. MCCount = 0;
  67. Screen = scr;
  68. PrevCursor = NULL;
  69. MouseUpdate = 0;
  70. State = 1;
  71. timeBeginPeriod ( 1000/ 60);
  72. InitializeCriticalSection (&MouseCriticalSection);
  73. //
  74. // Install the timer callback event handler
  75. //
  76. EraseBuffer = new char[mouse_max_width * mouse_max_height];
  77. EraseBuffX = -1;
  78. EraseBuffY = -1;
  79. EraseBuffHotX = -1;
  80. EraseBuffHotY = -1;
  81. EraseFlags = FALSE;
  82. _Mouse = this;
  83. TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC);
  84. /*
  85. ** Force the windows mouse pointer to stay withing the graphic view port region
  86. */
  87. Set_Cursor_Clip();
  88. }
  89. WWMouseClass::~WWMouseClass()
  90. {
  91. MouseUpdate++;
  92. if (MouseCursor) delete[] MouseCursor;
  93. if (MouseBuffer) delete[] MouseBuffer;
  94. if (TimerHandle) {
  95. timeKillEvent(TimerHandle);
  96. }
  97. timeEndPeriod (1000/60);
  98. DeleteCriticalSection(&MouseCriticalSection);
  99. /*
  100. ** Free up the windows mouse pointer movement
  101. */
  102. Clear_Cursor_Clip();
  103. }
  104. void Block_Mouse(GraphicBufferClass *buffer)
  105. {
  106. if (_Mouse){
  107. _Mouse->Block_Mouse(buffer);
  108. }
  109. }
  110. void Unblock_Mouse(GraphicBufferClass *buffer)
  111. {
  112. if (_Mouse){
  113. _Mouse->Unblock_Mouse(buffer);
  114. }
  115. }
  116. void WWMouseClass::Block_Mouse(GraphicBufferClass *buffer)
  117. {
  118. if (buffer == Screen->Get_Graphic_Buffer()){
  119. EnterCriticalSection(&MouseCriticalSection);
  120. }
  121. }
  122. void WWMouseClass::Unblock_Mouse(GraphicBufferClass *buffer)
  123. {
  124. if (buffer == Screen->Get_Graphic_Buffer()){
  125. LeaveCriticalSection(&MouseCriticalSection);
  126. }
  127. }
  128. void WWMouseClass::Set_Cursor_Clip(void)
  129. {
  130. if (Screen){
  131. RECT region;
  132. region.left = 0;
  133. region.top = 0;
  134. region.right = Screen->Get_Width();
  135. region.bottom = Screen->Get_Height();
  136. ClipCursor(&region);
  137. }
  138. }
  139. void WWMouseClass::Clear_Cursor_Clip(void)
  140. {
  141. ClipCursor(NULL);
  142. }
  143. void WWMouseClass::Process_Mouse(void)
  144. {
  145. POINT pt; // define a structure to hold current cursor pos
  146. //
  147. // If the mouse is currently hidden or it has not been installed, then we
  148. // have no need to redraw the mouse.
  149. //
  150. if (!Screen || !_Mouse || State > 0 || MouseUpdate || EraseFlags || !GameInFocus)
  151. return;
  152. //
  153. // Make sure there are no conflicts with other
  154. // threads that may try and lock the screen buffer
  155. //
  156. //Block_Mouse(Screen->Get_Graphic_Buffer());
  157. //
  158. // If the screen is already locked by another thread then just exit
  159. //
  160. if (Screen->Get_LockCount()!=0){
  161. //Unblock_Mouse(Screen->Get_Graphic_Buffer());
  162. return;
  163. }
  164. //
  165. // Get the mouse's current real cursor position
  166. //
  167. GetCursorPos(&pt); // get the current cursor position
  168. //
  169. // If the mouse has moved then we are responsible to redraw the mouse
  170. //
  171. if (pt.x != MouseBuffX || pt.y != MouseBuffY) {
  172. //
  173. // If we can't lock the surface we need to draw to, we cannot update
  174. // the mouse.
  175. //
  176. if (Screen->Lock()) {
  177. //
  178. // Erase the old mouse by dumping the mouses shadow buff
  179. // to the screen (if its position had ever been recorded).
  180. //
  181. Low_Hide_Mouse();
  182. //
  183. // Verify that the mouse has not gone into a conditional hiden area
  184. // If it has, mark it as being in one.
  185. //
  186. if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) {
  187. MCFlags |= CONDHIDDEN;
  188. }
  189. //
  190. // Show the mouse if we are allowed to.
  191. //
  192. if (!(MCFlags & CONDHIDDEN)) {
  193. Low_Show_Mouse(pt.x, pt.y);
  194. }
  195. //
  196. // Finally unlock the destination surface as we have sucessfully
  197. // updated the mouse.
  198. //
  199. Screen->Unlock();
  200. }
  201. }
  202. //
  203. // Allow other threads to lock the screen again
  204. //
  205. //Unblock_Mouse(Screen->Get_Graphic_Buffer());
  206. }
  207. void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, void *cursor)
  208. {
  209. //
  210. // If the pointer to the cursor we got is invalid, or its the same as the
  211. // currently set cursor then just return.
  212. if (!cursor || cursor == PrevCursor)
  213. return(cursor);
  214. //
  215. // Wait until we have exclusive access to our data
  216. //
  217. MouseUpdate++;
  218. //
  219. // Since we are updating the mouse we need to hide the cursor so we
  220. // do not get some sort of weird transformation.
  221. //
  222. Hide_Mouse();
  223. //
  224. // Now convert the shape to a mouse cursor with the given hotspots and
  225. // set it as our current mouse.
  226. //
  227. void *retval = ASM_Set_Mouse_Cursor(this, xhotspot, yhotspot, cursor);
  228. //
  229. // Show the mouse which will force it to appear with the new shape we
  230. // have assigned.
  231. //
  232. Show_Mouse();
  233. //
  234. // We are done updating the mouse cursor so on to bigger and better things.
  235. //
  236. MouseUpdate--;
  237. //
  238. // Return the previous mouse cursor which as conveniantly passed back by
  239. // Asm_Set_Mouse_Cursor.
  240. //
  241. return(retval);
  242. }
  243. void WWMouseClass::Low_Hide_Mouse()
  244. {
  245. if (!State) {
  246. if (MouseBuffX != -1 || MouseBuffY != -1) {
  247. if (Screen->Lock()){
  248. Mouse_Shadow_Buffer(this, Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0);
  249. Screen->Unlock();
  250. }
  251. }
  252. MouseBuffX = -1;
  253. MouseBuffY = -1;
  254. }
  255. State++;
  256. }
  257. void WWMouseClass::Hide_Mouse()
  258. {
  259. MouseUpdate++;
  260. Low_Hide_Mouse();
  261. MouseUpdate--;
  262. }
  263. void WWMouseClass::Low_Show_Mouse(int x, int y)
  264. {
  265. //
  266. // If the mouse is already visible then just ignore the problem.
  267. //
  268. if (State == 0) return;
  269. //
  270. // Make the mouse a little bit more visible
  271. //
  272. State--;
  273. //
  274. // If the mouse is completely visible then draw it at its current
  275. // position.
  276. //
  277. if (!State) {
  278. //
  279. // Try to lock the screen til we sucessfully get a lock.
  280. //
  281. if (Screen->Lock()){
  282. //
  283. // Save off the area behind the mouse.
  284. //
  285. Mouse_Shadow_Buffer(this, Screen, MouseBuffer, x, y, MouseXHot, MouseYHot, 1);
  286. //
  287. // Draw the mouse in its new location
  288. //
  289. ::Draw_Mouse(this, Screen, x, y);
  290. //
  291. // Save off the positions that we saved the buffer from
  292. //
  293. MouseBuffX = x;
  294. MouseBuffY = y;
  295. //
  296. // Unlock the screen and lets get moving.
  297. //
  298. Screen->Unlock();
  299. }
  300. }
  301. }
  302. void WWMouseClass::Show_Mouse()
  303. {
  304. POINT pt;
  305. GetCursorPos(&pt);
  306. MouseUpdate++;
  307. Low_Show_Mouse(pt.x, pt.y);
  308. MouseUpdate--;
  309. }
  310. void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2)
  311. {
  312. POINT pt;
  313. MouseUpdate++;
  314. //
  315. // First of all, adjust all the coordinates so that they handle
  316. // the fact that the hotspot is not necessarily the upper left
  317. // corner of the mouse.
  318. //
  319. x1 -= (CursorWidth - MouseXHot);
  320. x1 = MAX(0, x1);
  321. y1 -= (CursorHeight - MouseYHot);
  322. y1 = MAX(0, y1);
  323. x2 += MouseXHot;
  324. x2 = MIN(x2, Screen->Get_Width());
  325. y2 += MouseYHot;
  326. y2 = MIN(y2, Screen->Get_Height());
  327. // The mouse could be in one of four conditions.
  328. // 1) The mouse is visible and no conditional hide has been specified.
  329. // (perform normal region checking with possible hide)
  330. // 2) The mouse is hidden and no conditional hide as been specified.
  331. // (record region and do nothing)
  332. // 3) The mouse is visible and a conditional region has been specified
  333. // (expand region and perform check with possible hide).
  334. // 4) The mouse is already hidden by a previous conditional.
  335. // (expand region and do nothing)
  336. //
  337. // First: Set or expand the region according to the specified parameters
  338. if (!MCCount) {
  339. MouseCXLeft = x1;
  340. MouseCYUpper = y1;
  341. MouseCXRight = x2;
  342. MouseCYLower = y2;
  343. } else {
  344. MouseCXLeft = MIN(x1, MouseCXLeft);
  345. MouseCYUpper = MIN(y1, MouseCYUpper);
  346. MouseCXRight = MAX(x2, MouseCXRight);
  347. MouseCYLower = MAX(y2, MouseCYLower);
  348. }
  349. //
  350. // If the mouse isn't already hidden, then check its location against
  351. // the hiding region and hide if necessary.
  352. //
  353. if (!(MCFlags & CONDHIDDEN)) {
  354. GetCursorPos(&pt);
  355. if (MouseBuffX >= MouseCXLeft && MouseBuffX <= MouseCXRight && MouseBuffY >= MouseCYUpper && MouseBuffY <= MouseCYLower) {
  356. Low_Hide_Mouse();
  357. MCFlags |= CONDHIDDEN;
  358. }
  359. }
  360. //
  361. // Record the fact that a conditional hide was called and then exit
  362. //
  363. //
  364. MCFlags |= CONDHIDE;
  365. MCCount++;
  366. MouseUpdate--;
  367. }
  368. void WWMouseClass::Conditional_Show_Mouse(void)
  369. {
  370. MouseUpdate++;
  371. //
  372. // if there are any nested hides then dec the count
  373. //
  374. if (MCCount) {
  375. MCCount--;
  376. //
  377. // If the mouse is now not hidden and it had actually been
  378. // hidden before then display it.
  379. //
  380. if (!MCCount) {
  381. if (MCFlags & CONDHIDDEN) {
  382. Show_Mouse();
  383. }
  384. MCFlags = 0;
  385. }
  386. }
  387. MouseUpdate--;
  388. }
  389. void WWMouseClass::Draw_Mouse(GraphicViewPortClass *scr)
  390. {
  391. POINT pt;
  392. if (State != 0) return;
  393. MouseUpdate++;
  394. //
  395. // Get the position that the mouse is currently located at
  396. //
  397. GetCursorPos(&pt);
  398. if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) {
  399. Hide_Mouse();
  400. MCFlags |= CONDHIDDEN;
  401. } else {
  402. //
  403. // If the mouse is already visible then just ignore the problem.
  404. //
  405. EraseFlags = TRUE;
  406. //
  407. // Try to lock the screen - dont do video stuff if we cant.
  408. //
  409. if (scr->Lock()){
  410. //
  411. // Save off the area behind the mouse into two different buffers, one
  412. // which will be used to restore the mouse and the other which will
  413. // be used to restore the hidden surface when we get a chance.
  414. //
  415. Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1);
  416. memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight);
  417. //
  418. // Draw the mouse in its new location
  419. //
  420. ::Draw_Mouse(this, scr, pt.x, pt.y);
  421. //
  422. // Save off the positions that we saved the buffer from
  423. //
  424. EraseBuffX = pt.x;
  425. MouseBuffX = pt.x;
  426. EraseBuffY = pt.y;
  427. MouseBuffY = pt.y;
  428. EraseBuffHotX = MouseXHot;
  429. EraseBuffHotY = MouseYHot;
  430. //
  431. // Unlock the screen and lets get moving.
  432. //
  433. scr->Unlock();
  434. }
  435. }
  436. MouseUpdate--;
  437. }
  438. void WWMouseClass::Erase_Mouse(GraphicViewPortClass *scr, int forced)
  439. {
  440. //
  441. // If we are forcing the redraw of a mouse we already managed to
  442. // restore then just get outta here.
  443. //
  444. if (forced && EraseBuffX == -1 && EraseBuffY == -1) return;
  445. MouseUpdate++;
  446. //
  447. // If this is not a forced call, only update the mouse is we can legally
  448. // lock the buffer.
  449. //
  450. if (!forced) {
  451. #if(0)
  452. if (scr->Lock()) {
  453. //
  454. // If the surface has not already been restore then restore it and erase the
  455. // restoration coordinates so we don't accidentally do it twice.
  456. //
  457. if (EraseBuffX != -1 || EraseBuffY != -1) {
  458. Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0);
  459. EraseBuffX = -1;
  460. EraseBuffY = -1;
  461. }
  462. //
  463. // We are done writing to the buffer so unlock it.
  464. //
  465. scr->Unlock();
  466. }
  467. #endif
  468. } else {
  469. //
  470. // If the surface has not already been restore then restore it and erase the
  471. // restoration coordinates so we don't accidentally do it twice.
  472. //
  473. if (EraseBuffX != -1 || EraseBuffY != -1) {
  474. if (scr->Lock()){
  475. Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0);
  476. scr->Unlock();
  477. }
  478. EraseBuffX = -1;
  479. EraseBuffY = -1;
  480. }
  481. }
  482. MouseUpdate--;
  483. EraseFlags = FALSE;
  484. }
  485. int WWMouseClass::Get_Mouse_State(void)
  486. {
  487. return(State);
  488. }
  489. /***********************************************************************************************
  490. * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels *
  491. * *
  492. * INPUT: none *
  493. * *
  494. * OUTPUT: int - returns the mouses current x position in pixels *
  495. * *
  496. * HISTORY: *
  497. * 10/17/1995 PWG : Created. *
  498. *=============================================================================================*/
  499. int WWMouseClass::Get_Mouse_X(void)
  500. {
  501. POINT pt;
  502. GetCursorPos(&pt);
  503. return(pt.x);
  504. }
  505. /***********************************************************************************************
  506. * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels *
  507. * *
  508. * INPUT: none *
  509. * *
  510. * OUTPUT: int - returns the mouses current y position in pixels *
  511. * *
  512. * HISTORY: *
  513. * 10/17/1995 PWG : Created. *
  514. *=============================================================================================*/
  515. int WWMouseClass::Get_Mouse_Y(void)
  516. {
  517. POINT pt;
  518. GetCursorPos(&pt);
  519. return(pt.y);
  520. }
  521. /***********************************************************************************************
  522. * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars *
  523. * *
  524. * INPUT: int &x - variable to return the mouses x position in pixels *
  525. * int &y - variable to return the mouses y position in pixels *
  526. * *
  527. * OUTPUT: none - output is via reference variables *
  528. * *
  529. * HISTORY: *
  530. * 10/17/1995 PWG : Created. *
  531. *=============================================================================================*/
  532. void WWMouseClass::Get_Mouse_XY(int &x, int &y)
  533. {
  534. POINT pt;
  535. GetCursorPos(&pt);
  536. x = pt.x;
  537. y = pt.y;
  538. }
  539. #pragma off(unreferenced)
  540. void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 )
  541. {
  542. static BOOL in_mouse_callback = false;
  543. if (_Mouse && !in_mouse_callback) {
  544. in_mouse_callback = TRUE;
  545. _Mouse->Process_Mouse();
  546. in_mouse_callback = FALSE;
  547. }
  548. }
  549. #pragma on(unreferenced)
  550. void Hide_Mouse(void)
  551. {
  552. if (!_Mouse) return;
  553. _Mouse->Hide_Mouse();
  554. }
  555. void Show_Mouse(void)
  556. {
  557. if (!_Mouse) return;
  558. _Mouse->Show_Mouse();
  559. }
  560. void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2)
  561. {
  562. if (!_Mouse) return;
  563. _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2);
  564. }
  565. void Conditional_Show_Mouse(void)
  566. {
  567. if (!_Mouse) return;
  568. _Mouse->Conditional_Show_Mouse();
  569. }
  570. int Get_Mouse_State(void)
  571. {
  572. if (!_Mouse) return(0);
  573. return(_Mouse->Get_Mouse_State());
  574. }
  575. void *Set_Mouse_Cursor(int hotx, int hoty, void *cursor)
  576. {
  577. if (!_Mouse) return(0);
  578. return(_Mouse->Set_Cursor(hotx,hoty,cursor));
  579. }
  580. int Get_Mouse_X(void)
  581. {
  582. if (!_Mouse) return(0);
  583. return(_Mouse->Get_Mouse_X());
  584. }
  585. int Get_Mouse_Y(void)
  586. {
  587. if (!_Mouse) return(0);
  588. return(_Mouse->Get_Mouse_Y());
  589. }