some whitespace changes
[xonotic/darkplaces.git] / in_win.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the 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, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // in_win.c -- windows 95 mouse and joystick code
21 // 02/21/97 JCB Added extended DirectInput code to support external controllers.
22
23 #include <dinput.h>
24 #include "quakedef.h"
25 #include "winquake.h"
26 //#include "dosisms.h"
27
28 #define DINPUT_BUFFERSIZE           16
29 #define iDirectInputCreate(a,b,c,d)     pDirectInputCreate(a,b,c,d)
30
31 HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,
32         LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);
33
34 // mouse variables
35 cvar_t  m_filter = {"m_filter","0"};
36
37 int                     mouse_buttons;
38 int                     mouse_oldbuttonstate;
39 POINT           current_pos;
40 int                     mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
41
42 static qboolean restore_spi;
43 static int              originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
44
45 unsigned int uiWheelMessage;
46 qboolean        mouseactive;
47 qboolean                mouseinitialized;
48 static qboolean mouseparmsvalid, mouseactivatetoggle;
49 //static qboolean       mouseshowtoggle = 1;
50 static qboolean dinput_acquired;
51
52 static unsigned int             mstate_di;
53
54 // joystick defines and variables
55 // where should defines be moved?
56 #define JOY_ABSOLUTE_AXIS       0x00000000              // control like a joystick
57 #define JOY_RELATIVE_AXIS       0x00000010              // control like a mouse, spinner, trackball
58 #define JOY_MAX_AXES            6                               // X, Y, Z, R, U, V
59 #define JOY_AXIS_X                      0
60 #define JOY_AXIS_Y                      1
61 #define JOY_AXIS_Z                      2
62 #define JOY_AXIS_R                      3
63 #define JOY_AXIS_U                      4
64 #define JOY_AXIS_V                      5
65
66 enum _ControlList
67 {
68         AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn
69 };
70
71 DWORD   dwAxisFlags[JOY_MAX_AXES] =
72 {
73         JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
74 };
75
76 DWORD   dwAxisMap[JOY_MAX_AXES];
77 DWORD   dwControlMap[JOY_MAX_AXES];
78 PDWORD  pdwRawValue[JOY_MAX_AXES];
79
80 // none of these cvars are saved over a session
81 // this means that advanced controller configuration needs to be executed
82 // each time.  this avoids any problems with getting back to a default usage
83 // or when changing from one controller to another.  this way at least something
84 // works.
85 cvar_t  in_joystick = {"joystick","0", true};
86 cvar_t  joy_name = {"joyname", "joystick"};
87 cvar_t  joy_advanced = {"joyadvanced", "0"};
88 cvar_t  joy_advaxisx = {"joyadvaxisx", "0"};
89 cvar_t  joy_advaxisy = {"joyadvaxisy", "0"};
90 cvar_t  joy_advaxisz = {"joyadvaxisz", "0"};
91 cvar_t  joy_advaxisr = {"joyadvaxisr", "0"};
92 cvar_t  joy_advaxisu = {"joyadvaxisu", "0"};
93 cvar_t  joy_advaxisv = {"joyadvaxisv", "0"};
94 cvar_t  joy_forwardthreshold = {"joyforwardthreshold", "0.15"};
95 cvar_t  joy_sidethreshold = {"joysidethreshold", "0.15"};
96 cvar_t  joy_pitchthreshold = {"joypitchthreshold", "0.15"};
97 cvar_t  joy_yawthreshold = {"joyyawthreshold", "0.15"};
98 cvar_t  joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0"};
99 cvar_t  joy_sidesensitivity = {"joysidesensitivity", "-1.0"};
100 cvar_t  joy_pitchsensitivity = {"joypitchsensitivity", "1.0"};
101 cvar_t  joy_yawsensitivity = {"joyyawsensitivity", "-1.0"};
102 cvar_t  joy_wwhack1 = {"joywwhack1", "0.0"};
103 cvar_t  joy_wwhack2 = {"joywwhack2", "0.0"};
104
105 qboolean        joy_avail, joy_advancedinit, joy_haspov;
106 DWORD           joy_oldbuttonstate, joy_oldpovstate;
107
108 int                     joy_id;
109 DWORD           joy_flags;
110 DWORD           joy_numbuttons;
111
112 static LPDIRECTINPUT            g_pdi;
113 static LPDIRECTINPUTDEVICE      g_pMouse;
114
115 static JOYINFOEX        ji;
116
117 static HINSTANCE hInstDI;
118
119 static qboolean dinput;
120
121 typedef struct MYDATA {
122         LONG  lX;                   // X axis goes here
123         LONG  lY;                   // Y axis goes here
124         LONG  lZ;                   // Z axis goes here
125         BYTE  bButtonA;             // One button goes here
126         BYTE  bButtonB;             // Another button goes here
127         BYTE  bButtonC;             // Another button goes here
128         BYTE  bButtonD;             // Another button goes here
129 } MYDATA;
130
131 static DIOBJECTDATAFORMAT rgodf[] = {
132   { &GUID_XAxis,    FIELD_OFFSET(MYDATA, lX),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
133   { &GUID_YAxis,    FIELD_OFFSET(MYDATA, lY),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
134   { &GUID_ZAxis,    FIELD_OFFSET(MYDATA, lZ),       0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},
135   { 0,              FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
136   { 0,              FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
137   { 0,              FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
138   { 0,              FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
139 };
140
141 #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))
142
143 static DIDATAFORMAT     df = {
144         sizeof(DIDATAFORMAT),       // this structure
145         sizeof(DIOBJECTDATAFORMAT), // size of object data format
146         DIDF_RELAXIS,               // absolute axis coordinates
147         sizeof(MYDATA),             // device data size
148         NUM_OBJECTS,                // number of objects
149         rgodf,                      // and here they are
150 };
151
152 // forward-referenced functions
153 void IN_StartupJoystick (void);
154 void Joy_AdvancedUpdate_f (void);
155 void IN_JoyMove (usercmd_t *cmd);
156
157
158 /*
159 ===========
160 Force_CenterView_f
161 ===========
162 */
163 void Force_CenterView_f (void)
164 {
165         cl.viewangles[PITCH] = 0;
166 }
167
168
169 /*
170 ===========
171 IN_UpdateClipCursor
172 ===========
173 */
174 void IN_UpdateClipCursor (void)
175 {
176
177         if (mouseinitialized && mouseactive && !dinput)
178         {
179                 ClipCursor (&window_rect);
180         }
181 }
182
183
184 /*
185 ===========
186 IN_ShowMouse
187 ===========
188 */
189 void IN_ShowMouse (void)
190 {
191 //      if (!mouseshowtoggle)
192 //      {
193                 ShowCursor (true);
194 //              mouseshowtoggle = 1;
195 //      }
196 }
197
198
199 /*
200 ===========
201 IN_HideMouse
202 ===========
203 */
204 void IN_HideMouse (void)
205 {
206 //      if (mouseshowtoggle)
207 //      {
208                 ShowCursor (false);
209 //              mouseshowtoggle = 0;
210 //      }
211 }
212
213
214 /*
215 ===========
216 IN_ActivateMouse
217 ===========
218 */
219 void IN_ActivateMouse (void)
220 {
221
222         mouseactivatetoggle = true;
223
224         if (mouseinitialized)
225         {
226                 if (dinput)
227                 {
228                         if (g_pMouse)
229                         {
230                                 if (!dinput_acquired)
231                                 {
232                                         IDirectInputDevice_Acquire(g_pMouse);
233                                         dinput_acquired = true;
234                                 }
235                         }
236                         else
237                         {
238                                 return;
239                         }
240                 }
241                 else
242                 {
243                         if (mouseparmsvalid)
244                                 restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
245
246                         SetCursorPos (window_center_x, window_center_y);
247                         SetCapture (mainwindow);
248                         ClipCursor (&window_rect);
249                         
250                 }
251
252                 mouseactive = true;
253         }
254 }
255
256
257 /*
258 ===========
259 IN_DeactivateMouse
260 ===========
261 */
262 void IN_DeactivateMouse (void)
263 {
264
265         mouseactivatetoggle = false;
266
267         if (mouseinitialized)
268         {
269                 if (dinput)
270                 {
271                         if (g_pMouse)
272                         {
273                                 if (dinput_acquired)
274                                 {
275                                         IDirectInputDevice_Unacquire(g_pMouse);
276                                         dinput_acquired = false;
277                                 }
278                         }
279                 }
280                 else
281                 {
282                         if (restore_spi)
283                                 SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
284
285                         ClipCursor (NULL);
286                         ReleaseCapture ();
287                 }
288
289                 mouseactive = false;
290         }
291 }
292
293
294 /*
295 ===========
296 IN_InitDInput
297 ===========
298 */
299 qboolean IN_InitDInput (void)
300 {
301     HRESULT             hr;
302         DIPROPDWORD     dipdw = {
303                 {
304                         sizeof(DIPROPDWORD),        // diph.dwSize
305                         sizeof(DIPROPHEADER),       // diph.dwHeaderSize
306                         0,                          // diph.dwObj
307                         DIPH_DEVICE,                // diph.dwHow
308                 },
309                 DINPUT_BUFFERSIZE,              // dwData
310         };
311
312         if (!hInstDI)
313         {
314                 hInstDI = LoadLibrary("dinput.dll");
315                 
316                 if (hInstDI == NULL)
317                 {
318                         Con_SafePrintf ("Couldn't load dinput.dll\n");
319                         return false;
320                 }
321         }
322
323         if (!pDirectInputCreate)
324         {
325                 pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA");
326
327                 if (!pDirectInputCreate)
328                 {
329                         Con_SafePrintf ("Couldn't get DI proc addr\n");
330                         return false;
331                 }
332         }
333
334 // register with DirectInput and get an IDirectInput to play with.
335         hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
336
337         if (FAILED(hr))
338         {
339                 return false;
340         }
341
342 // obtain an interface to the system mouse device.
343         hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL);
344
345         if (FAILED(hr))
346         {
347                 Con_SafePrintf ("Couldn't open DI mouse device\n");
348                 return false;
349         }
350
351 // set the data format to "mouse format".
352         hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);
353
354         if (FAILED(hr))
355         {
356                 Con_SafePrintf ("Couldn't set DI mouse format\n");
357                 return false;
358         }
359
360 // set the cooperativity level.
361         hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,
362                         DISCL_EXCLUSIVE | DISCL_FOREGROUND);
363
364         if (FAILED(hr))
365         {
366                 Con_SafePrintf ("Couldn't set DI coop level\n");
367                 return false;
368         }
369
370
371 // set the buffer size to DINPUT_BUFFERSIZE elements.
372 // the buffer size is a DWORD property associated with the device
373         hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);
374
375         if (FAILED(hr))
376         {
377                 Con_SafePrintf ("Couldn't set DI buffersize\n");
378                 return false;
379         }
380
381         return true;
382 }
383
384
385 /*
386 ===========
387 IN_StartupMouse
388 ===========
389 */
390 void IN_StartupMouse (void)
391 {
392         if ( COM_CheckParm ("-nomouse") ) 
393                 return; 
394
395         mouseinitialized = true;
396
397         if (COM_CheckParm ("-dinput"))
398         {
399                 dinput = IN_InitDInput ();
400
401                 if (dinput)
402                 {
403                         Con_SafePrintf ("DirectInput initialized\n");
404                 }
405                 else
406                 {
407                         Con_SafePrintf ("DirectInput not initialized\n");
408                 }
409         }
410
411         if (!dinput)
412         {
413                 mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);
414
415                 if (mouseparmsvalid)
416                 {
417                         if ( COM_CheckParm ("-noforcemspd") ) 
418                                 newmouseparms[2] = originalmouseparms[2];
419
420                         if ( COM_CheckParm ("-noforcemaccel") ) 
421                         {
422                                 newmouseparms[0] = originalmouseparms[0];
423                                 newmouseparms[1] = originalmouseparms[1];
424                         }
425
426                         if ( COM_CheckParm ("-noforcemparms") ) 
427                         {
428                                 newmouseparms[0] = originalmouseparms[0];
429                                 newmouseparms[1] = originalmouseparms[1];
430                                 newmouseparms[2] = originalmouseparms[2];
431                         }
432                 }
433         }
434
435         mouse_buttons = 3;
436
437 // if a fullscreen video mode was set before the mouse was initialized,
438 // set the mouse state appropriately
439         if (mouseactivatetoggle)
440                 IN_ActivateMouse ();
441 }
442
443
444 /*
445 ===========
446 IN_Init
447 ===========
448 */
449 void IN_Init (void)
450 {
451         // mouse variables
452         Cvar_RegisterVariable (&m_filter);
453
454         // joystick variables
455         Cvar_RegisterVariable (&in_joystick);
456         Cvar_RegisterVariable (&joy_name);
457         Cvar_RegisterVariable (&joy_advanced);
458         Cvar_RegisterVariable (&joy_advaxisx);
459         Cvar_RegisterVariable (&joy_advaxisy);
460         Cvar_RegisterVariable (&joy_advaxisz);
461         Cvar_RegisterVariable (&joy_advaxisr);
462         Cvar_RegisterVariable (&joy_advaxisu);
463         Cvar_RegisterVariable (&joy_advaxisv);
464         Cvar_RegisterVariable (&joy_forwardthreshold);
465         Cvar_RegisterVariable (&joy_sidethreshold);
466         Cvar_RegisterVariable (&joy_pitchthreshold);
467         Cvar_RegisterVariable (&joy_yawthreshold);
468         Cvar_RegisterVariable (&joy_forwardsensitivity);
469         Cvar_RegisterVariable (&joy_sidesensitivity);
470         Cvar_RegisterVariable (&joy_pitchsensitivity);
471         Cvar_RegisterVariable (&joy_yawsensitivity);
472         Cvar_RegisterVariable (&joy_wwhack1);
473         Cvar_RegisterVariable (&joy_wwhack2);
474
475         Cmd_AddCommand ("force_centerview", Force_CenterView_f);
476         Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f);
477
478         uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" );
479
480         IN_StartupMouse ();
481         IN_StartupJoystick ();
482 }
483
484 /*
485 ===========
486 IN_Shutdown
487 ===========
488 */
489 void IN_Shutdown (void)
490 {
491 //      usingmouse = false;
492         IN_DeactivateMouse ();
493         IN_ShowMouse ();
494
495     if (g_pMouse)
496         {
497                 IDirectInputDevice_Release(g_pMouse);
498                 g_pMouse = NULL;
499         }
500
501     if (g_pdi)
502         {
503                 IDirectInput_Release(g_pdi);
504                 g_pdi = NULL;
505         }
506 }
507
508
509 /*
510 ===========
511 IN_MouseEvent
512 ===========
513 */
514 void IN_MouseEvent (int mstate)
515 {
516         int     i;
517
518         if (mouseactive && !dinput)
519         {
520         // perform button actions
521                 for (i=0 ; i<mouse_buttons ; i++)
522                 {
523                         if ( (mstate & (1<<i)) &&
524                                 !(mouse_oldbuttonstate & (1<<i)) )
525                         {
526                                 Key_Event (K_MOUSE1 + i, true);
527                         }
528
529                         if ( !(mstate & (1<<i)) &&
530                                 (mouse_oldbuttonstate & (1<<i)) )
531                         {
532                                 Key_Event (K_MOUSE1 + i, false);
533                         }
534                 }       
535                         
536                 mouse_oldbuttonstate = mstate;
537         }
538 }
539
540
541 /*
542 ===========
543 IN_MouseMove
544 ===========
545 */
546 void IN_MouseMove (usercmd_t *cmd)
547 {
548         int                                     i, mx, my, mouselook = (in_mlook.state & 1) || freelook.value;
549         DIDEVICEOBJECTDATA      od;
550         DWORD                           dwElements;
551         HRESULT                         hr;
552
553         if (!mouseactive)
554                 return;
555
556         if (dinput)
557         {
558                 mx = 0;
559                 my = 0;
560
561                 for (;;)
562                 {
563                         dwElements = 1;
564
565                         hr = IDirectInputDevice_GetDeviceData(g_pMouse,
566                                         sizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);
567
568                         if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))
569                         {
570                                 dinput_acquired = true;
571                                 IDirectInputDevice_Acquire(g_pMouse);
572                                 break;
573                         }
574
575                         /* Unable to read data or no data available */
576                         if (FAILED(hr) || dwElements == 0)
577                         {
578                                 break;
579                         }
580
581                         /* Look at the element to see what happened */
582
583                         switch (od.dwOfs)
584                         {
585                                 case DIMOFS_X:
586                                         mx += od.dwData;
587                                         break;
588
589                                 case DIMOFS_Y:
590                                         my += od.dwData;
591                                         break;
592
593                                 case DIMOFS_BUTTON0:
594                                         if (od.dwData & 0x80)
595                                                 mstate_di |= 1;
596                                         else
597                                                 mstate_di &= ~1;
598                                         break;
599
600                                 case DIMOFS_BUTTON1:
601                                         if (od.dwData & 0x80)
602                                                 mstate_di |= (1<<1);
603                                         else
604                                                 mstate_di &= ~(1<<1);
605                                         break;
606                                         
607                                 case DIMOFS_BUTTON2:
608                                         if (od.dwData & 0x80)
609                                                 mstate_di |= (1<<2);
610                                         else
611                                                 mstate_di &= ~(1<<2);
612                                         break;
613                         }
614                 }
615
616         // perform button actions
617                 for (i=0 ; i<mouse_buttons ; i++)
618                 {
619                         if ( (mstate_di & (1<<i)) &&
620                                 !(mouse_oldbuttonstate & (1<<i)) )
621                         {
622                                 Key_Event (K_MOUSE1 + i, true);
623                         }
624
625                         if ( !(mstate_di & (1<<i)) &&
626                                 (mouse_oldbuttonstate & (1<<i)) )
627                         {
628                                 Key_Event (K_MOUSE1 + i, false);
629                         }
630                 }       
631                         
632                 mouse_oldbuttonstate = mstate_di;
633         }
634         else
635         {
636                 GetCursorPos (&current_pos);
637                 mx = current_pos.x - window_center_x + mx_accum;
638                 my = current_pos.y - window_center_y + my_accum;
639                 mx_accum = 0;
640                 my_accum = 0;
641         }
642
643 //if (mx ||  my)
644 //      Con_DPrintf("mx=%d, my=%d\n", mx, my);
645
646         if (m_filter.value)
647         {
648                 mouse_x = (mx + old_mouse_x) * 0.5;
649                 mouse_y = (my + old_mouse_y) * 0.5;
650         }
651         else
652         {
653                 mouse_x = mx;
654                 mouse_y = my;
655         }
656
657         old_mouse_x = mx;
658         old_mouse_y = my;
659
660         mouse_x *= sensitivity.value;
661         mouse_y *= sensitivity.value;
662
663 // add mouse X/Y movement to cmd
664         if ( (in_strafe.state & 1) || (lookstrafe.value && mouselook))
665                 cmd->sidemove += m_side.value * mouse_x;
666         else
667                 cl.viewangles[YAW] -= m_yaw.value * mouse_x;
668
669         if (mouselook)
670                 V_StopPitchDrift ();
671         
672         // LordHavoc: changed limits on pitch from -70 to 80, to -90 to 90
673         if (mouselook && !(in_strafe.state & 1))
674         {
675                 cl.viewangles[PITCH] += m_pitch.value * mouse_y;
676                 if (cl.viewangles[PITCH] > 90)
677                         cl.viewangles[PITCH] = 90;
678                 if (cl.viewangles[PITCH] < -90)
679                         cl.viewangles[PITCH] = -90;
680         }
681         else
682         {
683                 if ((in_strafe.state & 1) && noclip_anglehack)
684                         cmd->upmove -= m_forward.value * mouse_y;
685                 else
686                         cmd->forwardmove -= m_forward.value * mouse_y;
687         }
688
689 // if the mouse has moved, force it to the center, so there's room to move
690         if (mx || my)
691         {
692                 SetCursorPos (window_center_x, window_center_y);
693         }
694 }
695
696
697 /*
698 ===========
699 IN_Move
700 ===========
701 */
702 void IN_Move (usercmd_t *cmd)
703 {
704
705         if (ActiveApp && !Minimized)
706         {
707                 IN_MouseMove (cmd);
708                 IN_JoyMove (cmd);
709         }
710 }
711
712
713 /*
714 ===========
715 IN_Accumulate
716 ===========
717 */
718 void IN_Accumulate (void)
719 {
720         if (mouseactive)
721         {
722                 if (!dinput)
723                 {
724                         GetCursorPos (&current_pos);
725
726                         mx_accum += current_pos.x - window_center_x;
727                         my_accum += current_pos.y - window_center_y;
728
729                 // force the mouse to the center, so there's room to move
730                         SetCursorPos (window_center_x, window_center_y);
731                 }
732         }
733 }
734
735
736 /*
737 ===================
738 IN_ClearStates
739 ===================
740 */
741 void IN_ClearStates (void)
742 {
743
744         if (mouseactive)
745         {
746                 mx_accum = 0;
747                 my_accum = 0;
748                 mouse_oldbuttonstate = 0;
749         }
750 }
751
752
753 /* 
754 =============== 
755 IN_StartupJoystick 
756 =============== 
757 */  
758 void IN_StartupJoystick (void) 
759
760         int                     numdevs;
761         JOYCAPS         jc;
762         MMRESULT        mmr;
763  
764         // assume no joystick
765         joy_avail = false; 
766
767         // abort startup if user requests no joystick
768         if ( COM_CheckParm ("-nojoy") ) 
769                 return; 
770  
771         // verify joystick driver is present
772         if ((numdevs = joyGetNumDevs ()) == 0)
773         {
774                 Con_Printf ("\njoystick not found -- driver not present\n\n");
775                 return;
776         }
777
778         // cycle through the joystick ids for the first valid one
779         for (joy_id=0 ; joy_id<numdevs ; joy_id++)
780         {
781                 memset (&ji, 0, sizeof(ji));
782                 ji.dwSize = sizeof(ji);
783                 ji.dwFlags = JOY_RETURNCENTERED;
784
785                 if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
786                         break;
787         } 
788
789         // abort startup if we didn't find a valid joystick
790         if (mmr != JOYERR_NOERROR)
791         {
792                 Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
793                 return;
794         }
795
796         // get the capabilities of the selected joystick
797         // abort startup if command fails
798         memset (&jc, 0, sizeof(jc));
799         if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
800         {
801                 Con_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); 
802                 return;
803         }
804
805         // save the joystick's number of buttons and POV status
806         joy_numbuttons = jc.wNumButtons;
807         joy_haspov = jc.wCaps & JOYCAPS_HASPOV;
808
809         // old button and POV states default to no buttons pressed
810         joy_oldbuttonstate = joy_oldpovstate = 0;
811
812         // mark the joystick as available and advanced initialization not completed
813         // this is needed as cvars are not available during initialization
814
815         joy_avail = true; 
816         joy_advancedinit = false;
817
818         Con_Printf ("\njoystick detected\n\n"); 
819 }
820
821
822 /*
823 ===========
824 RawValuePointer
825 ===========
826 */
827 PDWORD RawValuePointer (int axis)
828 {
829         switch (axis)
830         {
831         case JOY_AXIS_X:
832                 return &ji.dwXpos;
833         case JOY_AXIS_Y:
834                 return &ji.dwYpos;
835         case JOY_AXIS_Z:
836                 return &ji.dwZpos;
837         case JOY_AXIS_R:
838                 return &ji.dwRpos;
839         case JOY_AXIS_U:
840                 return &ji.dwUpos;
841         case JOY_AXIS_V:
842                 return &ji.dwVpos;
843         }
844         return NULL; // LordHavoc: hush compiler warning
845 }
846
847
848 /*
849 ===========
850 Joy_AdvancedUpdate_f
851 ===========
852 */
853 void Joy_AdvancedUpdate_f (void)
854 {
855
856         // called once by IN_ReadJoystick and by user whenever an update is needed
857         // cvars are now available
858         int     i;
859         DWORD dwTemp;
860
861         // initialize all the maps
862         for (i = 0; i < JOY_MAX_AXES; i++)
863         {
864                 dwAxisMap[i] = AxisNada;
865                 dwControlMap[i] = JOY_ABSOLUTE_AXIS;
866                 pdwRawValue[i] = RawValuePointer(i);
867         }
868
869         if( joy_advanced.value == 0.0)
870         {
871                 // default joystick initialization
872                 // 2 axes only with joystick control
873                 dwAxisMap[JOY_AXIS_X] = AxisTurn;
874                 // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
875                 dwAxisMap[JOY_AXIS_Y] = AxisForward;
876                 // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
877         }
878         else
879         {
880                 if (strcmp (joy_name.string, "joystick") != 0)
881                 {
882                         // notify user of advanced controller
883                         Con_Printf ("\n%s configured\n\n", joy_name.string);
884                 }
885
886                 // advanced initialization here
887                 // data supplied by user via joy_axisn cvars
888                 dwTemp = (DWORD) joy_advaxisx.value;
889                 dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
890                 dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
891                 dwTemp = (DWORD) joy_advaxisy.value;
892                 dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
893                 dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
894                 dwTemp = (DWORD) joy_advaxisz.value;
895                 dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
896                 dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
897                 dwTemp = (DWORD) joy_advaxisr.value;
898                 dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
899                 dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
900                 dwTemp = (DWORD) joy_advaxisu.value;
901                 dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
902                 dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
903                 dwTemp = (DWORD) joy_advaxisv.value;
904                 dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
905                 dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
906         }
907
908         // compute the axes to collect from DirectInput
909         joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
910         for (i = 0; i < JOY_MAX_AXES; i++)
911         {
912                 if (dwAxisMap[i] != AxisNada)
913                 {
914                         joy_flags |= dwAxisFlags[i];
915                 }
916         }
917 }
918
919
920 /*
921 ===========
922 IN_Commands
923 ===========
924 */
925 void IN_Commands (void)
926 {
927         int             i, key_index;
928         DWORD   buttonstate, povstate;
929
930         if (!joy_avail)
931         {
932                 return;
933         }
934
935         
936         // loop through the joystick buttons
937         // key a joystick event or auxillary event for higher number buttons for each state change
938         buttonstate = ji.dwButtons;
939         for (i=0 ; i < (int) joy_numbuttons ; i++)
940         {
941                 if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
942                 {
943                         key_index = (i < 4) ? K_JOY1 : K_AUX1;
944                         Key_Event (key_index + i, true);
945                 }
946
947                 if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
948                 {
949                         key_index = (i < 4) ? K_JOY1 : K_AUX1;
950                         Key_Event (key_index + i, false);
951                 }
952         }
953         joy_oldbuttonstate = buttonstate;
954
955         if (joy_haspov)
956         {
957                 // convert POV information into 4 bits of state information
958                 // this avoids any potential problems related to moving from one
959                 // direction to another without going through the center position
960                 povstate = 0;
961                 if(ji.dwPOV != JOY_POVCENTERED)
962                 {
963                         if (ji.dwPOV == JOY_POVFORWARD)
964                                 povstate |= 0x01;
965                         if (ji.dwPOV == JOY_POVRIGHT)
966                                 povstate |= 0x02;
967                         if (ji.dwPOV == JOY_POVBACKWARD)
968                                 povstate |= 0x04;
969                         if (ji.dwPOV == JOY_POVLEFT)
970                                 povstate |= 0x08;
971                 }
972                 // determine which bits have changed and key an auxillary event for each change
973                 for (i=0 ; i < 4 ; i++)
974                 {
975                         if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
976                         {
977                                 Key_Event (K_AUX29 + i, true);
978                         }
979
980                         if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
981                         {
982                                 Key_Event (K_AUX29 + i, false);
983                         }
984                 }
985                 joy_oldpovstate = povstate;
986         }
987 }
988
989
990 /* 
991 =============== 
992 IN_ReadJoystick
993 =============== 
994 */  
995 qboolean IN_ReadJoystick (void)
996 {
997
998         memset (&ji, 0, sizeof(ji));
999         ji.dwSize = sizeof(ji);
1000         ji.dwFlags = joy_flags;
1001
1002         if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
1003         {
1004                 // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver
1005                 // rather than having 32768 be the zero point, they have the zero point at 32668
1006                 // go figure -- anyway, now we get the full resolution out of the device
1007                 if (joy_wwhack1.value != 0.0)
1008                 {
1009                         ji.dwUpos += 100;
1010                 }
1011                 return true;
1012         }
1013         else
1014         {
1015                 // read error occurred
1016                 // turning off the joystick seems too harsh for 1 read error,\
1017                 // but what should be done?
1018                 // Con_Printf ("IN_ReadJoystick: no response\n");
1019                 // joy_avail = false;
1020                 return false;
1021         }
1022 }
1023
1024
1025 /*
1026 ===========
1027 IN_JoyMove
1028 ===========
1029 */
1030 void IN_JoyMove (usercmd_t *cmd)
1031 {
1032         float   speed, aspeed;
1033         float   fAxisValue, fTemp;
1034         int             i, mouselook = (in_mlook.state & 1) || freelook.value;
1035
1036         // complete initialization if first time in
1037         // this is needed as cvars are not available at initialization time
1038         if( joy_advancedinit != true )
1039         {
1040                 Joy_AdvancedUpdate_f();
1041                 joy_advancedinit = true;
1042         }
1043
1044         // verify joystick is available and that the user wants to use it
1045         if (!joy_avail || !in_joystick.value)
1046         {
1047                 return; 
1048         }
1049  
1050         // collect the joystick data, if possible
1051         if (IN_ReadJoystick () != true)
1052         {
1053                 return;
1054         }
1055
1056         if (in_speed.state & 1)
1057                 speed = cl_movespeedkey.value;
1058         else
1059                 speed = 1;
1060         aspeed = speed * host_realframetime;
1061
1062         // loop through the axes
1063         for (i = 0; i < JOY_MAX_AXES; i++)
1064         {
1065                 // get the floating point zero-centered, potentially-inverted data for the current axis
1066                 fAxisValue = (float) *pdwRawValue[i];
1067                 // move centerpoint to zero
1068                 fAxisValue -= 32768.0;
1069
1070                 if (joy_wwhack2.value != 0.0)
1071                 {
1072                         if (dwAxisMap[i] == AxisTurn)
1073                         {
1074                                 // this is a special formula for the Logitech WingMan Warrior
1075                                 // y=ax^b; where a = 300 and b = 1.3
1076                                 // also x values are in increments of 800 (so this is factored out)
1077                                 // then bounds check result to level out excessively high spin rates
1078                                 fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3);
1079                                 if (fTemp > 14000.0)
1080                                         fTemp = 14000.0;
1081                                 // restore direction information
1082                                 fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp;
1083                         }
1084                 }
1085
1086                 // convert range from -32768..32767 to -1..1 
1087                 fAxisValue /= 32768.0;
1088
1089                 switch (dwAxisMap[i])
1090                 {
1091                 case AxisForward:
1092                         if ((joy_advanced.value == 0.0) && mouselook)
1093                         {
1094                                 // user wants forward control to become look control
1095                                 if (fabs(fAxisValue) > joy_pitchthreshold.value)
1096                                 {               
1097                                         // if mouse invert is on, invert the joystick pitch value
1098                                         // only absolute control support here (joy_advanced is false)
1099                                         if (m_pitch.value < 0.0)
1100                                         {
1101                                                 cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1102                                         }
1103                                         else
1104                                         {
1105                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1106                                         }
1107                                         V_StopPitchDrift();
1108                                 }
1109                                 else
1110                                 {
1111                                         // no pitch movement
1112                                         // disable pitch return-to-center unless requested by user
1113                                         // *** this code can be removed when the lookspring bug is fixed
1114                                         // *** the bug always has the lookspring feature on
1115                                         if(lookspring.value == 0.0)
1116                                                 V_StopPitchDrift();
1117                                 }
1118                         }
1119                         else
1120                         {
1121                                 // user wants forward control to be forward control
1122                                 if (fabs(fAxisValue) > joy_forwardthreshold.value)
1123                                 {
1124                                         cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value;
1125                                 }
1126                         }
1127                         break;
1128
1129                 case AxisSide:
1130                         if (fabs(fAxisValue) > joy_sidethreshold.value)
1131                         {
1132                                 cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
1133                         }
1134                         break;
1135
1136                 case AxisTurn:
1137                         if ((in_strafe.state & 1) || (lookstrafe.value && mouselook))
1138                         {
1139                                 // user wants turn control to become side control
1140                                 if (fabs(fAxisValue) > joy_sidethreshold.value)
1141                                 {
1142                                         cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value;
1143                                 }
1144                         }
1145                         else
1146                         {
1147                                 // user wants turn control to be turn control
1148                                 if (fabs(fAxisValue) > joy_yawthreshold.value)
1149                                 {
1150                                         if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
1151                                         {
1152                                                 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value;
1153                                         }
1154                                         else
1155                                         {
1156                                                 cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0;
1157                                         }
1158
1159                                 }
1160                         }
1161                         break;
1162
1163                 case AxisLook:
1164                         if (mouselook)
1165                         {
1166                                 if (fabs(fAxisValue) > joy_pitchthreshold.value)
1167                                 {
1168                                         // pitch movement detected and pitch movement desired by user
1169                                         if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
1170                                         {
1171                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value;
1172                                         }
1173                                         else
1174                                         {
1175                                                 cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0;
1176                                         }
1177                                         V_StopPitchDrift();
1178                                 }
1179                                 else
1180                                 {
1181                                         // no pitch movement
1182                                         // disable pitch return-to-center unless requested by user
1183                                         // *** this code can be removed when the lookspring bug is fixed
1184                                         // *** the bug always has the lookspring feature on
1185                                         if(lookspring.value == 0.0)
1186                                                 V_StopPitchDrift();
1187                                 }
1188                         }
1189                         break;
1190
1191                 default:
1192                         break;
1193                 }
1194         }
1195
1196         // bounds check pitch
1197         if (cl.viewangles[PITCH] > 80.0)
1198                 cl.viewangles[PITCH] = 80.0;
1199         if (cl.viewangles[PITCH] < -70.0)
1200                 cl.viewangles[PITCH] = -70.0;
1201 }