2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Io/Mouse.cpp $
15 * Routines to read the mouse.
18 * Revision 1.7 2003/05/25 02:30:42 taylor
21 * Revision 1.6 2002/07/13 06:46:48 theoddone33
24 * Revision 1.5 2002/06/09 04:41:22 relnev
25 * added copyright header
27 * Revision 1.4 2002/06/02 04:26:34 relnev
30 * Revision 1.3 2002/05/29 06:25:13 theoddone33
31 * Keyboard input, mouse tracking now work
33 * Revision 1.2 2002/05/07 03:16:46 theoddone33
34 * The Great Newline Fix
36 * Revision 1.1.1.1 2002/05/03 03:28:09 root
40 * 4 7/15/99 9:20a Andsager
41 * FS2_DEMO initial checkin
43 * 3 6/02/99 6:18p Dave
44 * Fixed TNT lockup problems! Wheeeee!
46 * 2 10/07/98 10:53a Dave
49 * 1 10/07/98 10:49a Dave
51 * 29 6/10/98 2:52p Hoffoss
52 * Made mouse code use DI by default, but fall back on normal code if that
55 * 28 5/24/98 1:35p Hoffoss
56 * Fixed bug where mouse cursor is always recentering with a
57 * mouse_flush() call in debug version.
59 * 27 5/22/98 4:50p Hoffoss
60 * Trying to fix mouse acceleration problem..
62 * 26 5/21/98 12:26p Lawrance
63 * Fixed mouse jerk at mission start while in debug build only.
65 * 25 5/15/98 2:41p Hoffoss
66 * Made mouse default to off (for flying ship) and fixed some new pilot
69 * 24 5/08/98 4:13p Hoffoss
70 * Fixed problem with mouse pointer centering causing lost keypresses.
72 * 23 5/07/98 6:58p Hoffoss
73 * Made changes to mouse code to fix a number of problems.
75 * 22 5/05/98 8:38p Hoffoss
76 * Added sensitivity adjustment to options menu and made it save to pilot
79 * 21 5/05/98 1:03p Hoffoss
80 * Fixed initialization bug.
82 * 20 5/01/98 5:45p Hoffoss
83 * Made further improvements to the mouse code.
85 * 19 5/01/98 3:35p Hoffoss
86 * Made changes to release version of mouse code.
88 * 18 5/01/98 1:14p Hoffoss
89 * Changed mouse usage so directInput is only used for release version.
91 * 17 4/30/98 5:40p Hoffoss
92 * Added mouse as a supported control to fly the ship.
94 * 16 4/29/98 12:13a Lawrance
95 * Add function to check down count of mouse button without reseting the
96 * internal count. Added hook to reset demo trailer timer when a button
99 * 15 4/02/98 5:26p John
101 * 14 1/19/98 6:15p John
102 * Fixed all my Optimized Build compiler warnings
104 * 13 12/04/97 3:47p John
105 * Made joystick move mouse cursor
107 * 12 11/20/97 5:36p Dave
108 * Hooked in a bunch of main hall changes (including sound). Made it
109 * possible to reposition (rewind/ffwd)
110 * sound buffer pointers. Fixed animation direction change framerate
113 * 11 5/12/97 11:41a John
114 * Added range checking to mouse position
116 * 10 4/22/97 5:55p Lawrance
117 * let mouse.cpp decide if mouse has moved
119 * 9 4/22/97 12:29p John
120 * Changed mouse code so that you have to call mouse_init for the mouse
123 * 8 4/22/97 10:56a John
124 * fixed some resource leaks.
126 * 7 3/26/97 10:52a Lawrance
127 * mouse always on in menus, disappears in gameplay after 1 second
129 * 6 3/11/97 1:37p Lawrance
130 * added mouse_up_count(), changed mouse_mark() to mouse_mark_button() &
133 * 5 12/09/96 1:29p Lawrance
134 * adding 3 button support
136 * 4 12/03/96 4:19p John
137 * Added some code so that holding down the mouse buttons between menus
138 * doesn't select the next menu.
145 #include <windowsx.h>
152 #define MOUSE_MODE_DI 0
153 #define MOUSE_MODE_WIN 1
156 LOCAL int Mouse_mode = MOUSE_MODE_DI;
158 LOCAL int Mouse_mode = MOUSE_MODE_WIN;
161 LOCAL int mouse_inited = 0;
163 LOCAL int Di_mouse_inited = 0;
168 CRITICAL_SECTION mouse_lock;
170 // #define USE_DIRECTINPUT
173 int mouse_left_pressed = 0;
174 int mouse_right_pressed = 0;
175 int mouse_middle_pressed = 0;
176 int mouse_left_up = 0;
177 int mouse_right_up = 0;
178 int mouse_middle_up = 0;
183 int Mouse_sensitivity = 4;
184 int Use_mouse_to_fly = 0;
185 int Mouse_hidden = 0;
186 int Keep_mouse_centered = 0;;
190 void mouse_force_pos(int x, int y);
191 void mouse_eval_deltas_di();
193 int mouse_is_visible()
195 return !Mouse_hidden;
203 #ifdef USE_DIRECTINPUT
210 DeleteCriticalSection( &mouse_lock );
217 if ( mouse_inited ) return;
223 InitializeCriticalSection( &mouse_lock );
226 ENTER_CRITICAL_SECTION(&mouse_lock);
229 Mouse_x = gr_screen.max_w / 2;
230 Mouse_y = gr_screen.max_h / 2;
232 #ifdef USE_DIRECTINPUT
234 Mouse_mode = MOUSE_MODE_WIN;
236 Mouse_mode = MOUSE_MODE_WIN;
239 LEAVE_CRITICAL_SECTION(&mouse_lock);
241 atexit( mouse_close );
245 // ----------------------------------------------------------------------------
246 // mouse_mark_button() is called asynchronously by the OS when a mouse button
247 // goes up or down. The mouse button that is affected is passed via the
250 // parameters: flags ==> mouse button pressed/released
251 // set ==> 1 - button is pressed
252 // 0 - button is released
254 void mouse_mark_button( uint flags, int set)
256 if ( !mouse_inited ) return;
258 ENTER_CRITICAL_SECTION(&mouse_lock);
260 if ( !(mouse_flags & MOUSE_LEFT_BUTTON) ) {
262 if ( (flags & MOUSE_LEFT_BUTTON) && (set == 1) ) {
263 mouse_left_pressed++;
265 ////////////////////////////
266 /// SOMETHING TERRIBLE IS ABOUT TO HAPPEN. I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE
267 /// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK THE LEFT MOUSE BUTTON.
268 /// PLEASE SEE ALAN FOR MORE INFORMATION.
269 ////////////////////////////
270 #if defined(FS2_DEMO) || defined(FS1_DEMO)
272 extern void demo_reset_trailer_timer();
273 demo_reset_trailer_timer();
276 ////////////////////////////
277 /// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
278 ////////////////////////////
283 if ( (flags & MOUSE_LEFT_BUTTON) && (set == 0) ){
288 if ( !(mouse_flags & MOUSE_RIGHT_BUTTON) ) {
290 if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 1) ){
291 mouse_right_pressed++;
295 if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 0) ){
300 if ( !(mouse_flags & MOUSE_MIDDLE_BUTTON) ) {
302 if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 1) ){
303 mouse_middle_pressed++;
307 if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 0) ){
313 mouse_flags |= flags;
315 mouse_flags &= ~flags;
318 LEAVE_CRITICAL_SECTION(&mouse_lock);
327 Mouse_dx = Mouse_dy = Mouse_dz = 0;
328 ENTER_CRITICAL_SECTION(&mouse_lock);
329 mouse_left_pressed = 0;
330 mouse_right_pressed = 0;
331 mouse_middle_pressed = 0;
333 LEAVE_CRITICAL_SECTION(&mouse_lock);
336 int mouse_down_count(int n, int reset_count)
339 if ( !mouse_inited ) return 0;
341 if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0;
343 ENTER_CRITICAL_SECTION(&mouse_lock);
346 case MOUSE_LEFT_BUTTON:
347 tmp = mouse_left_pressed;
349 mouse_left_pressed = 0;
353 case MOUSE_RIGHT_BUTTON:
354 tmp = mouse_right_pressed;
356 mouse_right_pressed = 0;
360 case MOUSE_MIDDLE_BUTTON:
361 tmp = mouse_middle_pressed;
363 mouse_middle_pressed = 0;
368 LEAVE_CRITICAL_SECTION(&mouse_lock);
373 // mouse_up_count() returns the number of times button n has gone from down to up
374 // since the last call
376 // parameters: n - button of mouse (see #define's in mouse.h)
378 int mouse_up_count(int n)
381 if ( !mouse_inited ) return 0;
383 if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0;
385 ENTER_CRITICAL_SECTION(&mouse_lock);
388 case MOUSE_LEFT_BUTTON:
393 case MOUSE_RIGHT_BUTTON:
394 tmp = mouse_right_up;
398 case MOUSE_MIDDLE_BUTTON:
399 tmp = mouse_middle_up;
404 Assert(0); // can't happen
408 LEAVE_CRITICAL_SECTION(&mouse_lock);
413 // returns 1 if mouse button btn is down, 0 otherwise
415 int mouse_down(int btn)
418 if ( !mouse_inited ) return 0;
420 if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0;
423 ENTER_CRITICAL_SECTION(&mouse_lock);
426 if ( mouse_flags & btn )
431 LEAVE_CRITICAL_SECTION(&mouse_lock);
436 // returns the fraction of time btn has been down since last call
437 // (currently returns 1 if buttons is down, 0 otherwise)
439 float mouse_down_time(int btn)
442 if ( !mouse_inited ) return 0.0f;
444 if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0.0f;
446 ENTER_CRITICAL_SECTION(&mouse_lock);
448 if ( mouse_flags & btn )
453 LEAVE_CRITICAL_SECTION(&mouse_lock);
458 void mouse_get_delta(int *dx, int *dy, int *dz)
468 // Forces the actual windows cursor to be at (x,y). This may be independent of our tracked (x,y) mouse pos.
469 void mouse_force_pos(int x, int y)
471 if (os_foreground()) { // only mess with windows's mouse if we are in control of it
479 ClientToScreen((HWND) os_get_window(), &pnt);
480 SetCursorPos(pnt.x, pnt.y);
485 #include "gamesequence.h"
487 // change in mouse position since last call
488 void mouse_eval_deltas()
490 static int old_x = 0;
491 static int old_y = 0;
492 int tmp_x, tmp_y, cx, cy;
494 Mouse_dx = Mouse_dy = Mouse_dz = 0;
498 if (Mouse_mode == MOUSE_MODE_DI) {
499 mouse_eval_deltas_di();
503 cx = gr_screen.max_w / 2;
504 cy = gr_screen.max_h / 2;
506 ENTER_CRITICAL_SECTION(&mouse_lock);
509 SDL_GetMouseState (&tmp_x, &tmp_y);
513 ScreenToClient((HWND)os_get_window(), &pnt);
518 Mouse_dx = tmp_x - old_x;
519 Mouse_dy = tmp_y - old_y;
522 if (Keep_mouse_centered && Mouse_hidden) {
523 if (Mouse_dx || Mouse_dy)
524 mouse_force_pos(cx, cy);
534 LEAVE_CRITICAL_SECTION(&mouse_lock);
540 static LPDIRECTINPUT Di_mouse_obj = NULL;
541 static LPDIRECTINPUTDEVICE Di_mouse = NULL;
544 void mouse_eval_deltas_di()
551 DIMOUSESTATE mouse_state;
553 Mouse_dx = Mouse_dy = Mouse_dz = 0;
554 if (!Di_mouse_inited)
558 memset(&mouse_state, 0, sizeof(mouse_state));
562 hr = Di_mouse->GetDeviceState(sizeof(mouse_state), &mouse_state);
563 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
564 // DirectInput is telling us that the input stream has
565 // been interrupted. We aren't tracking any state
566 // between polls, so we don't have any special reset
567 // that needs to be done. We just re-acquire and
569 Sleep(500); // Pause a half second...
570 hr = Di_mouse->Acquire();
577 Mouse_dx = (int) mouse_state.lX;
578 Mouse_dy = (int) mouse_state.lY;
579 Mouse_dz = (int) mouse_state.lZ;
582 Mouse_dx = Mouse_dy = Mouse_dz = 0;
594 if (Mouse_x >= gr_screen.max_w)
595 Mouse_x = gr_screen.max_w - 1;
597 if (Mouse_y >= gr_screen.max_h)
598 Mouse_y = gr_screen.max_h - 1;
600 // keep the mouse inside our window so we don't switch applications or anything (debug bug people reported?)
601 // JH: Dang! This makes the mouse readings in DirectInput act screwy!
602 // mouse_force_pos(gr_screen.max_w / 2, gr_screen.max_h / 2);
606 int mouse_get_pos(int *xpos, int *ypos)
610 if (Mouse_mode == MOUSE_MODE_DI) {
626 flags = SDL_GetMouseState (&Mouse_x, &Mouse_y);
631 ScreenToClient((HWND)os_get_window(), &pnt);
633 // EnterCriticalSection(&mouse_lock);
640 // LeaveCriticalSection(&mouse_lock);
650 if (Mouse_x >= gr_screen.max_w){
651 Mouse_x = gr_screen.max_w - 1;
654 if (Mouse_y >= gr_screen.max_h){
655 Mouse_y = gr_screen.max_h - 1;
669 void mouse_get_real_pos(int *mx, int *my)
671 if (Mouse_mode == MOUSE_MODE_DI) {
678 SDL_GetMouseState (mx, my);
682 ScreenToClient((HWND)os_get_window(), &pnt);
689 void mouse_set_pos(int xpos, int ypos)
691 if (Mouse_mode == MOUSE_MODE_DI) {
697 if ((xpos != Mouse_x) || (ypos != Mouse_y)){
698 mouse_force_pos(xpos, ypos);
710 if (Mouse_mode == MOUSE_MODE_WIN){
715 hr = DirectInputCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &Di_mouse_obj, NULL);
717 hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_mouse_obj, NULL);
719 mprintf(( "DirectInputCreate() failed!\n" ));
724 hr = Di_mouse_obj->CreateDevice(GUID_SysMouse, &Di_mouse, NULL);
726 mprintf(( "CreateDevice() failed!\n" ));
730 hr = Di_mouse->SetDataFormat(&c_dfDIMouse);
732 mprintf(( "SetDataFormat() failed!\n" ));
736 hr = Di_mouse->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
738 mprintf(( "SetCooperativeLevel() failed!\n" ));
745 hdr.diph.dwSize = sizeof(DIPROPDWORD);
746 hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
748 hdr.diph.dwHow = DIPH_DEVICE; // Apply to entire device
749 hdr.dwData = 16; //MAX_BUFFERED_KEYBOARD_EVENTS;
751 hr = Di_mouse->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph );
753 mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" ));
757 Di_event = CreateEvent( NULL, FALSE, FALSE, NULL );
758 Assert(Di_event != NULL);
760 hr = Di_mouse->SetEventNotification(Di_event);
762 mprintf(( "SetEventNotification failed\n" ));
778 // Destroy any lingering IDirectInputDevice object.
780 // Unacquire the device one last time just in case we got really confused
781 // and tried to exit while the device is still acquired.
782 Di_mouse->Unacquire();
788 // Destroy any lingering IDirectInput object.
790 Di_mouse_obj->Release();