2 * $Logfile: /Freespace2/code/Io/Mouse.cpp $
7 * Routines to read the mouse.
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 4 7/15/99 9:20a Andsager
15 * FS2_DEMO initial checkin
17 * 3 6/02/99 6:18p Dave
18 * Fixed TNT lockup problems! Wheeeee!
20 * 2 10/07/98 10:53a Dave
23 * 1 10/07/98 10:49a Dave
25 * 29 6/10/98 2:52p Hoffoss
26 * Made mouse code use DI by default, but fall back on normal code if that
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.
33 * 27 5/22/98 4:50p Hoffoss
34 * Trying to fix mouse acceleration problem..
36 * 26 5/21/98 12:26p Lawrance
37 * Fixed mouse jerk at mission start while in debug build only.
39 * 25 5/15/98 2:41p Hoffoss
40 * Made mouse default to off (for flying ship) and fixed some new pilot
43 * 24 5/08/98 4:13p Hoffoss
44 * Fixed problem with mouse pointer centering causing lost keypresses.
46 * 23 5/07/98 6:58p Hoffoss
47 * Made changes to mouse code to fix a number of problems.
49 * 22 5/05/98 8:38p Hoffoss
50 * Added sensitivity adjustment to options menu and made it save to pilot
53 * 21 5/05/98 1:03p Hoffoss
54 * Fixed initialization bug.
56 * 20 5/01/98 5:45p Hoffoss
57 * Made further improvements to the mouse code.
59 * 19 5/01/98 3:35p Hoffoss
60 * Made changes to release version of mouse code.
62 * 18 5/01/98 1:14p Hoffoss
63 * Changed mouse usage so directInput is only used for release version.
65 * 17 4/30/98 5:40p Hoffoss
66 * Added mouse as a supported control to fly the ship.
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
73 * 15 4/02/98 5:26p John
75 * 14 1/19/98 6:15p John
76 * Fixed all my Optimized Build compiler warnings
78 * 13 12/04/97 3:47p John
79 * Made joystick move mouse cursor
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
87 * 11 5/12/97 11:41a John
88 * Added range checking to mouse position
90 * 10 4/22/97 5:55p Lawrance
91 * let mouse.cpp decide if mouse has moved
93 * 9 4/22/97 12:29p John
94 * Changed mouse code so that you have to call mouse_init for the mouse
97 * 8 4/22/97 10:56a John
98 * fixed some resource leaks.
100 * 7 3/26/97 10:52a Lawrance
101 * mouse always on in menus, disappears in gameplay after 1 second
103 * 6 3/11/97 1:37p Lawrance
104 * added mouse_up_count(), changed mouse_mark() to mouse_mark_button() &
107 * 5 12/09/96 1:29p Lawrance
108 * adding 3 button support
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.
119 #include <windowsx.h>
126 #define MOUSE_MODE_DI 0
127 #define MOUSE_MODE_WIN 1
130 LOCAL int Mouse_mode = MOUSE_MODE_DI;
132 LOCAL int Mouse_mode = MOUSE_MODE_WIN;
135 LOCAL int mouse_inited = 0;
136 LOCAL int Di_mouse_inited = 0;
140 CRITICAL_SECTION mouse_lock;
142 // #define USE_DIRECTINPUT
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;
155 int Mouse_sensitivity = 4;
156 int Use_mouse_to_fly = 0;
157 int Mouse_hidden = 0;
158 int Keep_mouse_centered = 0;;
162 void mouse_force_pos(int x, int y);
163 void mouse_eval_deltas_di();
165 int mouse_is_visible()
167 return !Mouse_hidden;
175 #ifdef USE_DIRECTINPUT
182 DeleteCriticalSection( &mouse_lock );
189 if ( mouse_inited ) return;
195 InitializeCriticalSection( &mouse_lock );
198 ENTER_CRITICAL_SECTION(&mouse_lock);
201 Mouse_x = gr_screen.max_w / 2;
202 Mouse_y = gr_screen.max_h / 2;
204 #ifdef USE_DIRECTINPUT
206 Mouse_mode = MOUSE_MODE_WIN;
208 Mouse_mode = MOUSE_MODE_WIN;
211 LEAVE_CRITICAL_SECTION(&mouse_lock);
213 atexit( mouse_close );
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
222 // parameters: flags ==> mouse button pressed/released
223 // set ==> 1 - button is pressed
224 // 0 - button is released
226 void mouse_mark_button( uint flags, int set)
228 if ( !mouse_inited ) return;
230 ENTER_CRITICAL_SECTION(&mouse_lock);
232 if ( !(mouse_flags & MOUSE_LEFT_BUTTON) ) {
234 if ( (flags & MOUSE_LEFT_BUTTON) && (set == 1) ) {
235 mouse_left_pressed++;
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 ////////////////////////////
244 extern void demo_reset_trailer_timer();
245 demo_reset_trailer_timer();
248 ////////////////////////////
249 /// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
250 ////////////////////////////
255 if ( (flags & MOUSE_LEFT_BUTTON) && (set == 0) ){
260 if ( !(mouse_flags & MOUSE_RIGHT_BUTTON) ) {
262 if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 1) ){
263 mouse_right_pressed++;
267 if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 0) ){
272 if ( !(mouse_flags & MOUSE_MIDDLE_BUTTON) ) {
274 if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 1) ){
275 mouse_middle_pressed++;
279 if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 0) ){
285 mouse_flags |= flags;
287 mouse_flags &= ~flags;
290 LEAVE_CRITICAL_SECTION(&mouse_lock);
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;
305 LEAVE_CRITICAL_SECTION(&mouse_lock);
308 int mouse_down_count(int n, int reset_count)
311 if ( !mouse_inited ) return 0;
313 if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0;
315 ENTER_CRITICAL_SECTION(&mouse_lock);
318 case MOUSE_LEFT_BUTTON:
319 tmp = mouse_left_pressed;
321 mouse_left_pressed = 0;
325 case MOUSE_RIGHT_BUTTON:
326 tmp = mouse_right_pressed;
328 mouse_right_pressed = 0;
332 case MOUSE_MIDDLE_BUTTON:
333 tmp = mouse_middle_pressed;
335 mouse_middle_pressed = 0;
340 LEAVE_CRITICAL_SECTION(&mouse_lock);
345 // mouse_up_count() returns the number of times button n has gone from down to up
346 // since the last call
348 // parameters: n - button of mouse (see #define's in mouse.h)
350 int mouse_up_count(int n)
353 if ( !mouse_inited ) return 0;
355 if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0;
357 ENTER_CRITICAL_SECTION(&mouse_lock);
360 case MOUSE_LEFT_BUTTON:
365 case MOUSE_RIGHT_BUTTON:
366 tmp = mouse_right_up;
370 case MOUSE_MIDDLE_BUTTON:
371 tmp = mouse_middle_up;
376 Assert(0); // can't happen
380 LEAVE_CRITICAL_SECTION(&mouse_lock);
385 // returns 1 if mouse button btn is down, 0 otherwise
387 int mouse_down(int btn)
390 if ( !mouse_inited ) return 0;
392 if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0;
395 ENTER_CRITICAL_SECTION(&mouse_lock);
398 if ( mouse_flags & btn )
403 LEAVE_CRITICAL_SECTION(&mouse_lock);
408 // returns the fraction of time btn has been down since last call
409 // (currently returns 1 if buttons is down, 0 otherwise)
411 float mouse_down_time(int btn)
414 if ( !mouse_inited ) return 0.0f;
416 if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0.0f;
418 ENTER_CRITICAL_SECTION(&mouse_lock);
420 if ( mouse_flags & btn )
425 LEAVE_CRITICAL_SECTION(&mouse_lock);
430 void mouse_get_delta(int *dx, int *dy, int *dz)
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)
443 if (os_foreground()) { // only mess with windows's mouse if we are in control of it
451 ClientToScreen((HWND) os_get_window(), &pnt);
452 SetCursorPos(pnt.x, pnt.y);
457 #include "gamesequence.h"
459 // change in mouse position since last call
460 void mouse_eval_deltas()
462 static int old_x = 0;
463 static int old_y = 0;
464 int tmp_x, tmp_y, cx, cy;
466 Mouse_dx = Mouse_dy = Mouse_dz = 0;
470 if (Mouse_mode == MOUSE_MODE_DI) {
471 mouse_eval_deltas_di();
475 cx = gr_screen.max_w / 2;
476 cy = gr_screen.max_h / 2;
478 ENTER_CRITICAL_SECTION(&mouse_lock);
485 ScreenToClient((HWND)os_get_window(), &pnt);
490 Mouse_dx = tmp_x - old_x;
491 Mouse_dy = tmp_y - old_y;
494 if (Keep_mouse_centered && Mouse_hidden) {
495 if (Mouse_dx || Mouse_dy)
496 mouse_force_pos(cx, cy);
506 LEAVE_CRITICAL_SECTION(&mouse_lock);
512 static LPDIRECTINPUT Di_mouse_obj = NULL;
513 static LPDIRECTINPUTDEVICE Di_mouse = NULL;
516 void mouse_eval_deltas_di()
523 DIMOUSESTATE mouse_state;
525 Mouse_dx = Mouse_dy = Mouse_dz = 0;
526 if (!Di_mouse_inited)
530 memset(&mouse_state, 0, sizeof(mouse_state));
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
541 Sleep(500); // Pause a half second...
542 hr = Di_mouse->Acquire();
549 Mouse_dx = (int) mouse_state.lX;
550 Mouse_dy = (int) mouse_state.lY;
551 Mouse_dz = (int) mouse_state.lZ;
554 Mouse_dx = Mouse_dy = Mouse_dz = 0;
566 if (Mouse_x >= gr_screen.max_w)
567 Mouse_x = gr_screen.max_w - 1;
569 if (Mouse_y >= gr_screen.max_h)
570 Mouse_y = gr_screen.max_h - 1;
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);
578 int mouse_get_pos(int *xpos, int *ypos)
582 if (Mouse_mode == MOUSE_MODE_DI) {
602 ScreenToClient((HWND)os_get_window(), &pnt);
604 // EnterCriticalSection(&mouse_lock);
611 // LeaveCriticalSection(&mouse_lock);
621 if (Mouse_x >= gr_screen.max_w){
622 Mouse_x = gr_screen.max_w - 1;
625 if (Mouse_y >= gr_screen.max_h){
626 Mouse_y = gr_screen.max_h - 1;
640 void mouse_get_real_pos(int *mx, int *my)
642 if (Mouse_mode == MOUSE_MODE_DI) {
653 ScreenToClient((HWND)os_get_window(), &pnt);
660 void mouse_set_pos(int xpos, int ypos)
662 if (Mouse_mode == MOUSE_MODE_DI) {
668 if ((xpos != Mouse_x) || (ypos != Mouse_y)){
669 mouse_force_pos(xpos, ypos);
681 if (Mouse_mode == MOUSE_MODE_WIN){
686 hr = DirectInputCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &Di_mouse_obj, NULL);
688 hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_mouse_obj, NULL);
690 mprintf(( "DirectInputCreate() failed!\n" ));
695 hr = Di_mouse_obj->CreateDevice(GUID_SysMouse, &Di_mouse, NULL);
697 mprintf(( "CreateDevice() failed!\n" ));
701 hr = Di_mouse->SetDataFormat(&c_dfDIMouse);
703 mprintf(( "SetDataFormat() failed!\n" ));
707 hr = Di_mouse->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
709 mprintf(( "SetCooperativeLevel() failed!\n" ));
716 hdr.diph.dwSize = sizeof(DIPROPDWORD);
717 hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
719 hdr.diph.dwHow = DIPH_DEVICE; // Apply to entire device
720 hdr.dwData = 16; //MAX_BUFFERED_KEYBOARD_EVENTS;
722 hr = Di_mouse->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph );
724 mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" ));
728 Di_event = CreateEvent( NULL, FALSE, FALSE, NULL );
729 Assert(Di_event != NULL);
731 hr = Di_mouse->SetEventNotification(Di_event);
733 mprintf(( "SetEventNotification failed\n" ));
749 // Destroy any lingering IDirectInputDevice object.
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();
759 // Destroy any lingering IDirectInput object.
761 Di_mouse_obj->Release();