]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/mouse.cpp
Initial revision
[taylor/freespace2.git] / src / io / mouse.cpp
1 /*
2  * $Logfile: /Freespace2/code/Io/Mouse.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Routines to read the mouse.
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 4     7/15/99 9:20a Andsager
15  * FS2_DEMO initial checkin
16  * 
17  * 3     6/02/99 6:18p Dave
18  * Fixed TNT lockup problems! Wheeeee!
19  * 
20  * 2     10/07/98 10:53a Dave
21  * Initial checkin.
22  * 
23  * 1     10/07/98 10:49a Dave
24  * 
25  * 29    6/10/98 2:52p Hoffoss
26  * Made mouse code use DI by default, but fall back on normal code if that
27  * fails.
28  * 
29  * 28    5/24/98 1:35p Hoffoss
30  * Fixed bug where  mouse cursor is always recentering with a
31  * mouse_flush() call in debug version.
32  * 
33  * 27    5/22/98 4:50p Hoffoss
34  * Trying to fix mouse acceleration problem..
35  * 
36  * 26    5/21/98 12:26p Lawrance
37  * Fixed mouse jerk at mission start while in debug build only.
38  * 
39  * 25    5/15/98 2:41p Hoffoss
40  * Made mouse default to off (for flying ship) and fixed some new pilot
41  * init bugs.
42  * 
43  * 24    5/08/98 4:13p Hoffoss
44  * Fixed problem with mouse pointer centering causing lost keypresses.
45  * 
46  * 23    5/07/98 6:58p Hoffoss
47  * Made changes to mouse code to fix a number of problems.
48  * 
49  * 22    5/05/98 8:38p Hoffoss
50  * Added sensitivity adjustment to options menu and made it save to pilot
51  * file.
52  * 
53  * 21    5/05/98 1:03p Hoffoss
54  * Fixed initialization bug.
55  * 
56  * 20    5/01/98 5:45p Hoffoss
57  * Made further improvements to the mouse code.
58  * 
59  * 19    5/01/98 3:35p Hoffoss
60  * Made changes to release version of mouse code.
61  * 
62  * 18    5/01/98 1:14p Hoffoss
63  * Changed mouse usage so directInput is only used for release version.
64  * 
65  * 17    4/30/98 5:40p Hoffoss
66  * Added mouse as a supported control to fly the ship.
67  * 
68  * 16    4/29/98 12:13a Lawrance
69  * Add function to check down count of mouse button without reseting the
70  * internal count.  Added hook to reset demo trailer timer when a button
71  * is pressed.
72  * 
73  * 15    4/02/98 5:26p John
74  * 
75  * 14    1/19/98 6:15p John
76  * Fixed all my Optimized Build compiler warnings
77  * 
78  * 13    12/04/97 3:47p John
79  * Made joystick move mouse cursor
80  * 
81  * 12    11/20/97 5:36p Dave
82  * Hooked in a bunch of main hall changes (including sound). Made it
83  * possible to reposition (rewind/ffwd) 
84  * sound buffer pointers. Fixed animation direction change framerate
85  * problem.
86  * 
87  * 11    5/12/97 11:41a John
88  * Added range checking to mouse position
89  * 
90  * 10    4/22/97 5:55p Lawrance
91  * let mouse.cpp decide if mouse has moved
92  * 
93  * 9     4/22/97 12:29p John
94  * Changed mouse code so that you have to call mouse_init for the mouse
95  * stuff to work.
96  * 
97  * 8     4/22/97 10:56a John
98  * fixed some resource leaks.
99  * 
100  * 7     3/26/97 10:52a Lawrance
101  * mouse always on in menus, disappears in gameplay after 1 second
102  * 
103  * 6     3/11/97 1:37p Lawrance
104  * added mouse_up_count(), changed mouse_mark() to mouse_mark_button() &
105  * mouse_mark_move()
106  * 
107  * 5     12/09/96 1:29p Lawrance
108  * adding 3 button support
109  * 
110  * 4     12/03/96 4:19p John
111  * Added some code so that holding down the mouse buttons between menus
112  * doesn't select the next menu.
113  *
114  * $NoKeywords: $
115  */
116
117 #ifndef PLAT_UNIX
118 #include <windows.h>
119 #include <windowsx.h>
120 #endif
121
122 #include "mouse.h"
123 #include "2d.h"
124 #include "osapi.h"
125
126 #define MOUSE_MODE_DI   0
127 #define MOUSE_MODE_WIN  1
128
129 #ifdef NDEBUG
130 LOCAL int Mouse_mode = MOUSE_MODE_DI;
131 #else
132 LOCAL int Mouse_mode = MOUSE_MODE_WIN;
133 #endif
134
135 LOCAL int mouse_inited = 0;
136 LOCAL int Di_mouse_inited = 0;
137 LOCAL int Mouse_x;
138 LOCAL int Mouse_y;
139
140 CRITICAL_SECTION mouse_lock;
141
142 // #define USE_DIRECTINPUT
143
144 int mouse_flags;
145 int mouse_left_pressed = 0;
146 int mouse_right_pressed = 0;
147 int mouse_middle_pressed = 0;
148 int mouse_left_up = 0;
149 int mouse_right_up = 0;
150 int mouse_middle_up = 0;
151 int Mouse_dx = 0;
152 int Mouse_dy = 0;
153 int Mouse_dz = 0;
154
155 int Mouse_sensitivity = 4;
156 int Use_mouse_to_fly = 0;
157 int Mouse_hidden = 0;
158 int Keep_mouse_centered = 0;;
159
160 int di_init();
161 void di_cleanup();
162 void mouse_force_pos(int x, int y);
163 void mouse_eval_deltas_di();
164
165 int mouse_is_visible()
166 {
167         return !Mouse_hidden;
168 }
169
170 void mouse_close()
171 {
172         if (!mouse_inited)
173                 return;
174
175 #ifdef USE_DIRECTINPUT
176         di_cleanup();
177 #endif
178         mouse_inited = 0;
179 #ifdef PLAT_UNIX
180         STUB_FUNCTION;
181 #else
182         DeleteCriticalSection( &mouse_lock );
183 #endif
184 }
185
186 void mouse_init()
187 {
188         // Initialize queue
189         if ( mouse_inited ) return;
190         mouse_inited = 1;
191
192 #ifdef PLAT_UNIX
193         STUB_FUNCTION;
194 #else
195         InitializeCriticalSection( &mouse_lock );
196 #endif
197
198         ENTER_CRITICAL_SECTION(&mouse_lock);
199
200         mouse_flags = 0;
201         Mouse_x = gr_screen.max_w / 2;
202         Mouse_y = gr_screen.max_h / 2;
203
204 #ifdef USE_DIRECTINPUT
205         if (!di_init())
206                 Mouse_mode = MOUSE_MODE_WIN;
207 #else
208         Mouse_mode = MOUSE_MODE_WIN;
209 #endif
210
211         LEAVE_CRITICAL_SECTION(&mouse_lock);    
212
213         atexit( mouse_close );
214 }
215
216
217 // ----------------------------------------------------------------------------
218 // mouse_mark_button() is called asynchronously by the OS when a mouse button
219 // goes up or down.  The mouse button that is affected is passed via the 
220 // flags parameter.  
221 //
222 // parameters:   flags ==> mouse button pressed/released
223 //               set   ==> 1 - button is pressed
224 //                         0 - button is released
225
226 void mouse_mark_button( uint flags, int set)
227 {
228         if ( !mouse_inited ) return;
229
230         ENTER_CRITICAL_SECTION(&mouse_lock);
231
232         if ( !(mouse_flags & MOUSE_LEFT_BUTTON) )       {
233
234                 if ( (flags & MOUSE_LEFT_BUTTON) && (set == 1) ) {
235                         mouse_left_pressed++;
236
237 ////////////////////////////
238 /// SOMETHING TERRIBLE IS ABOUT TO HAPPEN.  I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE
239 /// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK THE LEFT MOUSE BUTTON.
240 /// PLEASE SEE ALAN FOR MORE INFORMATION.
241 ////////////////////////////
242 #ifdef FS2_DEMO
243                                         {
244                                         extern void demo_reset_trailer_timer();
245                                         demo_reset_trailer_timer();
246                                         }
247 #endif
248 ////////////////////////////
249 /// IT'S OVER.  SEE, IT WASN'T SO BAD RIGHT?  IT'S IS VERY UGLY LOOKING, I KNOW.
250 ////////////////////////////
251
252                 }
253         }
254         else {
255                 if ( (flags & MOUSE_LEFT_BUTTON) && (set == 0) ){
256                         mouse_left_up++;
257                 }
258         }
259
260         if ( !(mouse_flags & MOUSE_RIGHT_BUTTON) )      {
261
262                 if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 1) ){
263                         mouse_right_pressed++;
264                 }
265         }
266         else {
267                 if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 0) ){
268                         mouse_right_up++;
269                 }
270         }
271
272         if ( !(mouse_flags & MOUSE_MIDDLE_BUTTON) )     {
273
274                 if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 1) ){
275                         mouse_middle_pressed++;
276                 }
277         }
278         else {
279                 if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 0) ){
280                         mouse_middle_up++;
281                 }
282         }
283
284         if ( set ){
285                 mouse_flags |= flags;
286         } else {
287                 mouse_flags &= ~flags;
288         }
289
290         LEAVE_CRITICAL_SECTION(&mouse_lock);    
291 }
292
293 void mouse_flush()
294 {
295         if (!mouse_inited)
296                 return;
297
298         mouse_eval_deltas();
299         Mouse_dx = Mouse_dy = Mouse_dz = 0;
300         ENTER_CRITICAL_SECTION(&mouse_lock);
301         mouse_left_pressed = 0;
302         mouse_right_pressed = 0;
303         mouse_middle_pressed = 0;
304         mouse_flags = 0;
305         LEAVE_CRITICAL_SECTION(&mouse_lock);    
306 }
307
308 int mouse_down_count(int n, int reset_count)
309 {
310         int tmp = 0;
311         if ( !mouse_inited ) return 0;
312
313         if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0;
314
315         ENTER_CRITICAL_SECTION(&mouse_lock);
316
317         switch (n) {
318                 case MOUSE_LEFT_BUTTON:
319                         tmp = mouse_left_pressed;
320                         if ( reset_count ) {
321                                 mouse_left_pressed = 0;
322                         }
323                         break;
324
325                 case MOUSE_RIGHT_BUTTON:
326                         tmp = mouse_right_pressed;
327                         if ( reset_count ) {
328                                 mouse_right_pressed = 0;
329                         }
330                         break;
331
332                 case MOUSE_MIDDLE_BUTTON:
333                         tmp = mouse_middle_pressed;
334                         if ( reset_count ) {
335                                 mouse_middle_pressed = 0;
336                         }
337                         break;
338         } // end switch
339
340         LEAVE_CRITICAL_SECTION(&mouse_lock);    
341
342         return tmp;
343 }
344
345 // mouse_up_count() returns the number of times button n has gone from down to up
346 // since the last call
347 //
348 // parameters:  n - button of mouse (see #define's in mouse.h)
349 //
350 int mouse_up_count(int n)
351 {
352         int tmp = 0;
353         if ( !mouse_inited ) return 0;
354
355         if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0;
356
357         ENTER_CRITICAL_SECTION(&mouse_lock);
358
359         switch (n) {
360                 case MOUSE_LEFT_BUTTON:
361                         tmp = mouse_left_up;
362                         mouse_left_up = 0;
363                         break;
364
365                 case MOUSE_RIGHT_BUTTON:
366                         tmp = mouse_right_up;
367                         mouse_right_up = 0;
368                         break;
369
370                 case MOUSE_MIDDLE_BUTTON:
371                         tmp = mouse_middle_up;
372                         mouse_middle_up = 0;
373                         break;
374
375                 default:
376                         Assert(0);      // can't happen
377                         break;
378         } // end switch
379
380         LEAVE_CRITICAL_SECTION(&mouse_lock);    
381
382         return tmp;
383 }
384
385 // returns 1 if mouse button btn is down, 0 otherwise
386
387 int mouse_down(int btn)
388 {
389         int tmp;
390         if ( !mouse_inited ) return 0;
391
392         if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0;
393
394
395         ENTER_CRITICAL_SECTION(&mouse_lock);
396
397
398         if ( mouse_flags & btn )
399                 tmp = 1;
400         else
401                 tmp = 0;
402
403         LEAVE_CRITICAL_SECTION(&mouse_lock);    
404
405         return tmp;
406 }
407
408 // returns the fraction of time btn has been down since last call 
409 // (currently returns 1 if buttons is down, 0 otherwise)
410 //
411 float mouse_down_time(int btn)
412 {
413         float tmp;
414         if ( !mouse_inited ) return 0.0f;
415
416         if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0.0f;
417
418         ENTER_CRITICAL_SECTION(&mouse_lock);
419
420         if ( mouse_flags & btn )
421                 tmp = 1.0f;
422         else
423                 tmp = 0.0f;
424
425         LEAVE_CRITICAL_SECTION(&mouse_lock);
426
427         return tmp;
428 }
429
430 void mouse_get_delta(int *dx, int *dy, int *dz)
431 {
432         if (dx)
433                 *dx = Mouse_dx;
434         if (dy)
435                 *dy = Mouse_dy;
436         if (dz)
437                 *dz = Mouse_dz;
438 }
439
440 // Forces the actual windows cursor to be at (x,y).  This may be independent of our tracked (x,y) mouse pos.
441 void mouse_force_pos(int x, int y)
442 {
443         if (os_foreground()) {  // only mess with windows's mouse if we are in control of it
444 #ifdef PLAT_UNIX
445                 STUB_FUNCTION;
446 #else
447                 POINT pnt;
448
449                 pnt.x = x;
450                 pnt.y = y;
451                 ClientToScreen((HWND) os_get_window(), &pnt);
452                 SetCursorPos(pnt.x, pnt.y);
453 #endif
454         }
455 }
456
457 #include "gamesequence.h"
458
459 // change in mouse position since last call
460 void mouse_eval_deltas()
461 {
462         static int old_x = 0;
463         static int old_y = 0;
464         int tmp_x, tmp_y, cx, cy;
465
466         Mouse_dx = Mouse_dy = Mouse_dz = 0;
467         if (!mouse_inited)
468                 return;
469
470         if (Mouse_mode == MOUSE_MODE_DI) {
471                 mouse_eval_deltas_di();
472                 return;
473         }
474
475         cx = gr_screen.max_w / 2;
476         cy = gr_screen.max_h / 2;
477
478         ENTER_CRITICAL_SECTION(&mouse_lock);
479
480 #ifdef PLAT_UNIX
481         STUB_FUNCTION;
482 #else
483         POINT pnt;
484         GetCursorPos(&pnt);
485         ScreenToClient((HWND)os_get_window(), &pnt);
486         tmp_x = pnt.x;
487         tmp_y = pnt.y;
488 #endif
489
490         Mouse_dx = tmp_x - old_x;
491         Mouse_dy = tmp_y - old_y;
492         Mouse_dz = 0;
493
494         if (Keep_mouse_centered && Mouse_hidden) {
495                 if (Mouse_dx || Mouse_dy)
496                         mouse_force_pos(cx, cy);
497
498                 old_x = cx;
499                 old_y = cy;
500
501         } else {
502                 old_x = tmp_x;
503                 old_y = tmp_y;
504         }
505
506         LEAVE_CRITICAL_SECTION(&mouse_lock);
507 }
508
509 #ifndef PLAT_UNIX
510 #include "vdinput.h"
511
512 static LPDIRECTINPUT                    Di_mouse_obj = NULL;
513 static LPDIRECTINPUTDEVICE      Di_mouse = NULL;
514 #endif
515
516 void mouse_eval_deltas_di()
517 {
518 #ifdef PLAT_UNIX
519         STUB_FUNCTION;
520 #else
521         int repeat = 1;
522         HRESULT hr = 0;
523         DIMOUSESTATE mouse_state;
524
525         Mouse_dx = Mouse_dy = Mouse_dz = 0;
526         if (!Di_mouse_inited)
527                 return;
528
529         repeat = 1;
530         memset(&mouse_state, 0, sizeof(mouse_state));
531         while (repeat) {
532                 repeat = 0;
533
534                 hr = Di_mouse->GetDeviceState(sizeof(mouse_state), &mouse_state);
535                 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
536                         // DirectInput is telling us that the input stream has
537                         // been interrupted.  We aren't tracking any state
538                         // between polls, so we don't have any special reset
539                         // that needs to be done.  We just re-acquire and
540                         // try again.
541                         Sleep(500);             // Pause a half second...
542                         hr = Di_mouse->Acquire();
543                         if (SUCCEEDED(hr))
544                                 repeat = 1;
545                 }
546         }
547
548         if (SUCCEEDED(hr)) {
549                 Mouse_dx = (int) mouse_state.lX;
550                 Mouse_dy = (int) mouse_state.lY;
551                 Mouse_dz = (int) mouse_state.lZ;
552
553         } else {
554                 Mouse_dx = Mouse_dy = Mouse_dz = 0;
555         }
556
557         Mouse_x += Mouse_dx;
558         Mouse_y += Mouse_dy;
559
560         if (Mouse_x < 0)
561                 Mouse_x = 0;
562
563         if (Mouse_y < 0)
564                 Mouse_y = 0;
565
566         if (Mouse_x >= gr_screen.max_w)
567                 Mouse_x = gr_screen.max_w - 1;
568
569         if (Mouse_y >= gr_screen.max_h)
570                 Mouse_y = gr_screen.max_h - 1;
571
572         // keep the mouse inside our window so we don't switch applications or anything (debug bug people reported?)
573         // JH: Dang!  This makes the mouse readings in DirectInput act screwy!
574 //      mouse_force_pos(gr_screen.max_w / 2, gr_screen.max_h / 2);
575 #endif
576 }
577
578 int mouse_get_pos(int *xpos, int *ypos)
579 {
580         int flags;
581
582         if (Mouse_mode == MOUSE_MODE_DI) {
583                 if (xpos)
584                         *xpos = Mouse_x;
585
586                 if (ypos)
587                         *ypos = Mouse_y;
588
589                 return mouse_flags;
590         }
591
592         if (!mouse_inited) {
593                 *xpos = *ypos = 0;
594                 return 0;
595         }
596
597 #ifdef PLAT_UNIX
598         STUB_FUNCTION;
599 #else
600         POINT pnt;
601         GetCursorPos(&pnt);
602         ScreenToClient((HWND)os_get_window(), &pnt);
603
604 //      EnterCriticalSection(&mouse_lock);
605
606         flags = mouse_flags;
607         Mouse_x = pnt.x;
608         Mouse_y = pnt.y;
609 #endif
610
611 //      LeaveCriticalSection(&mouse_lock);
612
613         if (Mouse_x < 0){
614                 Mouse_x = 0;
615         }
616
617         if (Mouse_y < 0){
618                 Mouse_y = 0;
619         }
620
621         if (Mouse_x >= gr_screen.max_w){
622                 Mouse_x = gr_screen.max_w - 1;
623         }
624
625         if (Mouse_y >= gr_screen.max_h){
626                 Mouse_y = gr_screen.max_h - 1;
627         }
628         
629         if (xpos){
630                 *xpos = Mouse_x;
631         }
632
633         if (ypos){
634                 *ypos = Mouse_y;
635         }
636
637         return flags;
638 }
639
640 void mouse_get_real_pos(int *mx, int *my)
641 {
642         if (Mouse_mode == MOUSE_MODE_DI) {
643                 *mx = Mouse_x;
644                 *my = Mouse_y;
645                 return;
646         }
647
648 #ifdef PLAT_UNIX
649         STUB_FUNCTION;
650 #else
651         POINT pnt;
652         GetCursorPos(&pnt);
653         ScreenToClient((HWND)os_get_window(), &pnt);
654         
655         *mx = pnt.x;
656         *my = pnt.y;
657 #endif
658 }
659
660 void mouse_set_pos(int xpos, int ypos)
661 {
662         if (Mouse_mode == MOUSE_MODE_DI) {
663                 Mouse_x = xpos;
664                 Mouse_y = ypos;
665                 return;
666         }
667
668         if ((xpos != Mouse_x) || (ypos != Mouse_y)){
669                 mouse_force_pos(xpos, ypos);
670         }
671 }
672
673 int di_init()
674 {
675 #ifdef PLAT_UNIX
676         STUB_FUNCTION;
677         return 0;
678 #else
679         HRESULT hr;
680
681         if (Mouse_mode == MOUSE_MODE_WIN){
682                 return 0;
683         }
684
685         Di_mouse_inited = 0;
686         hr = DirectInputCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &Di_mouse_obj, NULL);
687         if (FAILED(hr)) {
688                 hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_mouse_obj, NULL);
689                 if (FAILED(hr)) {
690                         mprintf(( "DirectInputCreate() failed!\n" ));
691                         return FALSE;
692                 }
693         }
694
695         hr = Di_mouse_obj->CreateDevice(GUID_SysMouse, &Di_mouse, NULL);
696         if (FAILED(hr)) {
697                 mprintf(( "CreateDevice() failed!\n" ));
698                 return FALSE;
699         }
700
701         hr = Di_mouse->SetDataFormat(&c_dfDIMouse);
702         if (FAILED(hr)) {
703                 mprintf(( "SetDataFormat() failed!\n" ));
704                 return FALSE;
705         }
706
707         hr = Di_mouse->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
708         if (FAILED(hr)) {
709                 mprintf(( "SetCooperativeLevel() failed!\n" ));
710                 return FALSE;
711         }
712 /*
713         DIPROPDWORD hdr;
714
715         // Turn on buffering
716         hdr.diph.dwSize = sizeof(DIPROPDWORD); 
717         hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
718         hdr.diph.dwObj = 0;             
719         hdr.diph.dwHow = DIPH_DEVICE;   // Apply to entire device
720         hdr.dwData = 16;        //MAX_BUFFERED_KEYBOARD_EVENTS;
721
722         hr = Di_mouse->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph );
723         if (FAILED(hr)) {
724                 mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" ));
725                 return FALSE;
726         }
727
728         Di_event = CreateEvent( NULL, FALSE, FALSE, NULL );
729         Assert(Di_event != NULL);
730
731         hr = Di_mouse->SetEventNotification(Di_event);
732         if (FAILED(hr)) {
733                 mprintf(( "SetEventNotification failed\n" ));
734                 return FALSE;
735         }
736 */
737         Di_mouse->Acquire();
738
739         Di_mouse_inited = 1;
740         return TRUE;
741 #endif
742 }
743
744 void di_cleanup()
745 {
746 #ifdef PLAT_UNIX
747         STUB_FUNCTION;
748 #else
749         // Destroy any lingering IDirectInputDevice object.
750         if (Di_mouse) {
751                 // Unacquire the device one last time just in case we got really confused
752                 // and tried to exit while the device is still acquired.
753                 Di_mouse->Unacquire();
754
755                 Di_mouse->Release();
756                 Di_mouse = NULL;
757         }
758
759         // Destroy any lingering IDirectInput object.
760         if (Di_mouse_obj) {
761                 Di_mouse_obj->Release();
762                 Di_mouse_obj = NULL;
763         }
764
765         Di_mouse_inited = 0;
766 #endif
767 }