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