MOUSE.CPP 19 KB

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