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